how to compare two arrays and find the count of match elements - php

i have two arrays i.e$ar1=array("Mobile","shop","software","hardware");and$arr2=arry("shop","Mobile","shop","software","shop")
i want to compare the elements of arr2 to arr1 i.e
foreach($arr2 as $val)
{
if(in_array($val, $arr1))
{
//mycode to insert data into mysql table
variable++; // here there should be a variable that must be increamented when ever match found in $arr2 so that it can be saved into another table.
}
$query="update table set shop='$variable',Mobile='$variable'.......";
}
the $variable should be an integer value so that for later use i can use this variable(shop i.e in this example its value should be 3) to find the match.
My question is how can i get the variable that will increamented each time match found.

Sorry, I don't fully understand the purpose of your code. You can use array_intersect to get common values and array_diff to get the unique values when comparing two arrays.

i want to compare the elements of arr2 to arr1 i.e
Then you are essentially doing the same search for shop three times. It's inefficient. Why not sort and eliminate duplicates first?
Other issues. You are comparing arr2 values with the ones in arr1, which means the number of repetation for "shop" will not be 3 it will be one. Doing the opposite might give you the number of repetation of arr1[1] in arr2 =3.
There are multitude of ways to solve this problem. If efficiency is required,you might wish to sort so you don't have to go beyond a certain point (say s). You can learn to use indexes. Infact the whole datastructure is revolved around these kinds of things - from quick and dirty to efficient.

Not sure I understand the connection between your two arrays. But you can use this to count how many items are in your second array:
$items = array("shop","Mobile","shop","software","shop");
$count = array();
foreach($items as $item)
{
if(isset($count[$item]))
{
$count[$item]++;
}
else
{
$count[$item] = 1;
}
}
print_r($count); // Array ( [shop] => 3 [Mobile] => 1 [software] => 1 )

Related

Assign random but even amount from an array to a list array in PHP?

I have two arrays. One array $People currently creates number of 44 individuals. Lets just assume currently its
$People = array('1','2',...,'44');.
I have another array of 15 elements.
$Test = array('A','B',...'O');
Now I want to be able to assign the test randomly to each individual. I know how to do this using random function in php.
Where it has got tricky for me and what I need help with is how can I even out the test array. What I mean by this is since there are currently 44 individuals (this array will grow in future), what I want is 14 test versions to have 3 individuals and 1 version would have 2. So I want the test array to even out. I also want it to handle as growth of $People array.
Ex: Test Version D will have individual '4', '25'. Every other version has three random individuals.
Few ideas I came up with are things like running random integer on $Test array and which ever gets highest/lowest gets 2 individuals and rest three. This would give a problem when I increase the size of $People array; to deal with that I though about using modulus to figure out what will be even number of $Test beforehand.
I can do this and other ideas but am pretty sure there has to be a better way to do this.
Clarifying your situation:
You want to distribute the values inside $People randomly amongst your $Test array. The problem you stated you are having is that the amount of values in $People isn't always perfectly dividable by the amount of values in $Test, and you aren't sure how to go about implementing code to distribute the values evenly.
Proposed solution:
You could obtain the values in a foreach loop randomly 1 by 1 from a shuffled version of $People and put them in a new array called $Result. You would also have a conditional checking if you have extracted all the values from the shuffled $People array: if($count>=$arrayCount) where $arrayCount=$count($shuffledPeople);. If you have obtained all the values, you first make the $bool value false (in order not to iterate through the while loop anymore, and then you break; out of the foreach loop.
$Result =[];//the array containing the results
$shuffledPeople = $People;
shuffle($shuffledPeople);//mixing up the array
$arrayCount = count($shuffledPeople);//finding the total amount of people
$count = 0;
$bool = TRUE;
while ($bool)
{
foreach($Test as $value)
{
$Result[$value][] = $shuffledPeople[$count];
$count++;
if ($count>=$arrayCount)
{
$bool = FALSE;
break;
}
}
}
To view the results, all you would need to do is:
foreach ($Result as $key => $value)
{
echo "{$key}: <br>";
if (is_array($value))
{
foreach ($value as $something)
{
echo "-->{$something}<br>";
}
}
else
{
echo "-->{$value}<br>";
}
}
I believe that this is what you want to do...
Assume that you have $people and $test arrays. You want to know how many people per test...
$ppt = ceil(sizeof($people)/sizeof($test));
Now, $ppt is the people per test. The next step is to shuffle up the people so they are randomly assigned to the tests.
shuffle($people);
Now, we can chunk up the people into sub-arrays such that each sub-array is assigned to a test. Remember, the chunks are random now because we shuffled.
$chunks = array_chunk($people, $ppt);
Now, everyone in $chunks[0] will take $test[0]. Everyone in $chunks[1] will take $test[1]. Everyone in $chunks[2] will take $test[2].

How to (quickly) discover if two arrays have at least one common item

I am writing a script that will repeatedly search a large group of arrays (40,000) and merge all of the arrays that have at least one common element. I have tried array_intersect(), but I found that it was too slow for this application. Is there another function that is faster and simply returns true if at least one element is shared between two arrays?
It is my assumption that array_intersect() is slowed down by the fact that both arrays are reviewed completely and the commons values are grouped together and returned. It would be faster to exit when a single match is found.
To clarify: All arrays are held with an another master array (which is a 2d array.) If it is discovered that the arrays stored at $master[231] and $master[353] both contain the element 124352354, they should be merged into an new array and the result stored in a different 2d array designed to store merged results.
Current code:
$test = array_intersect($entry, $entry2);
if($test){
...
}
A better method is:
foreach($entry as $item){
if(in_array($item, $entry2)){
$test = true;
break;
}
}
if($test){
...
}
and another improvement is using isset() and array_flip() instead of in_array();
$flipped = array_flip($entry2);
foreach($entry as $item){
if(isset($flipped[$item]){
$test = true;
break;
}
}
if($test){
...
}
Assuming you want to just discover if two arrays have a common element, you could create your own getIntersect function which would be faster than using array_intersect since it would return instantly on first match.
function getIntersect($arr1, $arr2)
{
foreach($arr1 as $val1)
{
foreach($arr2 as $val2)
{
if($val1 == $val2)
{ return true; }
}
}
return false;
}
Assuming that what you really want to find is arrays in which one element occurs at least more than once.
Then you could easily have
function hasCommonElements($arr)
{
for($i = 0; $i < count($arr); $i++)
{
$val = $arr[$i];
unset($arr[$i]);
if(in_array($val, $arr))
{
return true;
}
}
}
And you could easily get an array of all arrays containing common elements using array_filter:
array_filter($my40k, "hasCommonElements");
Assuming that what you really want to do is to find all arrays which have at least one value in common, you have to do a higher level array filter.
$mybigarray;//your big array
function hasIntersects($arr)
{
for($i = 0; $i < count($mybigarray); $i++)
{
if(getIntersect($arr, $mybigarray[$i]))
{
return true;
}
}
}
Then call our filter monster
array_filter($mybigarray, "hasIntersects");
Disclaimer: None of this stuff is tested. Check for typos
If your arrays contain only values that are also valid keys (integers, ...), it could be better to flip the arrays (swap keys and values), which technically means build and index, and search on the keys. Examples:
function haveCommonValues1($a1, $a2) {
$a1_flipped = array_flip($a1);
foreach ($a2 as $val) {
if (isset($a1_flipped[$val])) {
return true;
}
}
return false;
}
or if you need the intersection:
function haveCommonValues2($a1, $a2) {
$a1_flipped = array_flip($a1);
$a2_flipped = array_flip($a2);
return array_intersect_key($a1_flipped, $a2_flipped);
}
On some test arrays i got these results, this however depends highly on the array structures. So you need to test it and compare the times.
array_intersect : 0m1.175s
haveCommonValues1 : 0m0.454s
haveCommonValues2 : 0m0.492s
Busting-up pre-conceived notions all around
I both proved my point, and undermined it, in one answer. TL;DR = Do this in SQL.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PHP built-ins are code-level cleaner, but surprisingly inefficient.
If you are going by key, your best bet would be the tried and true for-loop:
$array1 = array( ... );
$array2 = array( ... );
$match = FALSE;
foreach($array1 as $key => $value) {
if(isset($array2[$key]) && $array2[$key] == $value) {
$match = TRUE;
break;
}
}
It may seem counter-intuitive, but at the execution level, no mater what, you have to iterate over every item in at least one array. You can keep this shorter by only doing it on the shorter array.
array_intersect() keeps going for every key in both arrays, so, although it looks crazy, you just have to do it the "dirty" way.
If the data comes from a database, it would actually be faster to have the SQL engine do the lifting. A simple join with a limit 1 will give you a flag to know if there are duplicates, and then you can execute another query to get the merged data (dynamically generate the query against multiple tables or source queries if you need to do this on more than one pair).
SQL will be magnitudes faster than any higher-level language, like PHP, for doing this. I don't care if you already have the arrays in memory, executing the query and loading the new array from the DB will be faster than trying to do the compare and then merge in resident memory of the App...
Again, with counter-intuitive things...
So, this is interesting:
I made a test script for this at http://pastebin.com/rzeQnyu2
With the matcher (phone number) at 5 digits, the foreach loop consistently executed in 1/100 the time of the other option. HOWEVER, up that to 10 digits (removing all possibility of collision) and the foreach jumps to 36x slower than the other option.
# 5 digit key (phone number)
Match found
For Each completed in 0.0001
Intersect Key completed in 0.0113
# 10 digit key
Match not found
For Each completed in 0.2342
Intersect Key completed in 0.0064
I wonder why the second option (which likely had larger arrays) was faster for Intersect than the smaller one... WEIRD...
This is because, while the intersect always iterates over all items, the foreach loop wins when it can exit early, but looks to be VERY slow if it doesn't get that opportunity. I would be interested in the deeper technical reasons for this.
EIther Way - in the end, just do it in SQL.
array_intersect has a runtime of O(n * log(n)) because it uses a sorting algorithm before the comparison itself. Depending on your input you can improve that in many different ways (e.g. if you have integers from a small range you may impelement the algorithm using counting sort).
You can find an example for that optimizations right here or here.
A possible solution where you won't need sorting is posted in this thread. It also has linear time so I guess this is what your are looking for.
SELECT *
FROM contacts t1 INNER JOIN contacts t2
ON t1.phone = t2.phone
AND t1.AccountID < t2.AccountID
Also, if your system may ever grow to include international phone numbers you should store them as a string type. There are countries, I believe in Europe, that use leading zeroes in their phone numbers and you cannot properly store them with a numeric type.
edit
The below query will return all instances of phone numbers used multiple times with no duplicate rows no matter how many accounts are sharing a phone number:
SELECT DISTINCT t1.AccountID, t1.phone
FROM contacts t1 INNER JOIN contacts t2
ON t1.phone = t2.phone
AND t1.AccountID != t2.AccountID
ORDER by t1.phone
I'd include a SQLfiddle, but it seems to be broken atm. This is the schema/data I used as a test:
CREATE TABLE IF NOT EXISTS `contacts` (
`AccountID` int(11) NOT NULL,
`phone` varchar(32) NOT NULL,
KEY `aid` (`AccountID`),
KEY `phn` (`phone`)
)
INSERT INTO `contacts` (`AccountID`, `phone`) VALUES
(6, 'b'),
(1, 'b'),
(1, 'c'),
(2, 'd'),
(2, 'e'),
(3, 'f'),
(3, 'a'),
(4, 'a'),
(5, 'a'),
(1, 'a');

Find index of value in associative array in php?

If you have any array $p that you populated in a loop like so:
$p[] = array( "id"=>$id, "Name"=>$name);
What's the fastest way to search for John in the Name key, and if found, return the $p index? Is there a way other than looping through $p?
I have up to 5000 names to find in $p, and $p can also potentially contain 5000 rows. Currently I loop through $p looking for each name, and if found, parse it (and add it to another array), splice the row out of $p, and break 1, ready to start searching for the next of the 5000 names.
I was wondering if there if a faster way to get the index rather than looping through $p eg an isset type way?
Thanks for taking a look guys.
Okay so as I see this problem, you have unique ids, but the names may not be unique.
You could initialize the array as:
array($id=>$name);
And your searches can be like:
array_search($name,$arr);
This will work very well as native method of finding a needle in a haystack will have a better implementation than your own implementation.
e.g.
$id = 2;
$name= 'Sunny';
$arr = array($id=>$name);
echo array_search($name,$arr);
Echoes 2
The major advantage in this method would be code readability.
If you know that you are going to need to perform many of these types of search within the same request then you can create an index array from them. This will loop through the array once per index you need to create.
$piName = array();
foreach ($p as $k=>$v)
{
$piName[$v['Name']] = $k;
}
If you only need to perform one or two searches per page then consider moving the array into an external database, and creating the index there.
$index = 0;
$search_for = 'John';
$result = array_reduce($p, function($r, $v) use (&$index, $search_for) {
if($v['Name'] == $search_for) {
$r[] = $index;
}
++$index;
return $r;
});
$result will contain all the indices of elements in $p where the element with key Name had the value John. (This of course only works for an array that is indexed numerically beginning with 0 and has no “holes” in the index.)
Edit: Possibly even easier to just use array_filter, but that will not return the indices only, but all array element where Name equals John – but indices will be preserved:
$result2 = array_filter($p, function($elem) {
return $elem["Name"] == "John" ? true : false;
});
var_dump($result2);
What suits your needs better, resp. which one is maybe faster, is for you to figure out.

PHP – Retrieving values from multi-dimensional arrays

I have a working script, but I'm sure that my method of managing arrays could be better. I've searched for a solution and haven't found one, but I'm sure that I should be using the functionality of associative arrays to do things more efficiently.
I have two arrays, one from a CSV file and one from a DB. I've created the CSV array as numeric and the DB array as associative (although I'm aware that the difference is blurry in PHP).
I'm trying to find a record in the DB array where the value in one field matches a value in the CSV array. Both arrays are multi-dimensional.
Within each record in each array there is a reference number. It appears once in the CSV array and may appear in the DB array. If it does, I need to take action.
I'm currently doing this (simplified):
$CSVarray:
('reference01', 'blue', 'small' ),
('reference02', 'red', 'large' ),
('reference03', 'pink', 'medium' )
$Dbarray:
(0 => array(ref=>'reference01',name=>"tom",type=>"mouse"),
(1 => array(ref=>'reference02',name=>"jerry",type=>"cat"),
(2 => array(ref=>'reference03',name=>"butch",type=>"dog"),
foreach ($CSVarray as $CSVrecord) {
foreach ($Dbarray as $DBrecord) {
if ($CSVarray[$numerickey] == $DBrecord['key'] {
do something with the various values in the $DBrecord
}
}
}
This is horrible, as the arrays are each thousands of lines.
I don't just want to know if matching values exist, I want to retrieve data from the matching record, so functions like 'array_search ' don't do what I want and array_walk doesn't seem any better than my current approach.
What I really need is something like this (gibberish code):
foreach ($CSVarray as $CSVrecord) {
WHERE $Dbarray['key']['key'] == $CSVrecord[$numerickey] {
do something with the other values in $Dbarray['key']
}
}
I'm looking for a way to match the values using the keys (either numeric or associative) rather than walking the arrays. Can anyone offer any help please?
use a hash map - take one array and map each key of the record it belongs to, to that record. Then take the second array and simply iterate over it, checking for each record key if the hashmap has anything set for it.
Regarding your example:
foreach ($DBarray as $DBrecord){
$Hash[$record[$key]] = $DBrecord;
}
foreach ($CSVarray as $record){
if (isset($Hash[$record[$CSVkey]])){
$DBrecord = $Hash[$record[$CSVkey]];
//do stuff with $DBrecord and $CSVrecord
}
}
this solution works at O(n) while yours at O(n^2)...
You can use foreach loops like this too:
foreach ($record as $key => $value) {
switch($key)
{
case 'asd':
// do something
break;
default:
// Default
break;
}
}
A switch may be what you are looking for also :)
Load CSV into the db, and use db (not db array) if possible for retrieval. Index the referenceid field.

$_POST multidemensional array looping

So I've created a form wich, by the post function, send me several values.
$_POST[groups] sends me, using 4 different checkboxes values 1, 2, 4 and 8.
$_POST[retailgroup] does the same, but only the values 1, 2 and 4.
The problem is that I want these to add up together and then send the sum of the values to a MySQL table.
I've managed to do one using the following foreach loop:
foreach ( $_POST as $val ) {
foreach ( $val as $key=>$final_val ) {
$group = $group+$final_val;
}
}
The problem starts when I need to have these two apart from each other since they are going into separate columns in the table.
To be clear I've assigned different groups different values (always taking the prev value times two, just like counting binary) like 1, 2, 4 and 8. By adding them together I can determine membership in the groups by doing a subtraction "backwards". Since there are different kinds of groups I want them into two separate fields of the table.
I hope everything is clear.
Any idea on how to do this?
you want to use:
$groups_sum = array_sum($_POST['groups']);
$rgroups_sum = array_sum($_POST['retailgroups']);
This will be faster and clearer than a foreach loop in this case.
try
foreach ( $_POST[groups] as $key=>$final_val ) {
//do stuff for first group
}
foreach ( $_POST[retailgroup] as $key=>$final_val ) {
//do stuff for second group
}
and after you can make whatever you want with the variables got from loops
$groups = array_sum($_POST['groups']);
$retailGroups = array_sum($_POST['retailgroup']);
You can check membership like this instead of subtracting backwards:
$groupAdmin = 4;
if($groups & $groupAdmin) {
// is admin
}

Categories