PHP - How do I partially compare elements in 2 arrays - php

I have 2 arrays:
$arr1 = array('Test', 'Hello', 'World', 'Foo', 'Bar1', 'Bar'); and
$arr2 = array('hello', 'Else', 'World', 'Tes', 'foo', 'BaR1', 'Bar');
I need to compare the 2 arrays and save the position of the matching elements to a 3rd array $arr3 = (3, 0, 2, 4, 5, 6); //expected result, displaying position of matching element of $arr1 in $arr2.
By 'matching' I mean all elements that are identical (ex. World), or partially the same (ex. Test & Tes) and also those elements that are alike but are in different case (ex. Foo & foo, Bar & bar).
I've tried a series of combinations and of various functions without success, using functions like array_intersect(), substr_compare(), array_filter() and more. I'm not asking for the exact solution, just something to get me on the right track because i'm going around in circles all afternoon.

This seemed to work for me, but I'm sure there are some edge cases I'm missing that you'd need to test for:
foreach( $arr1 as $i => $val1) {
$result = null;
// Search the second array for an exact match, if found
if( ($found = array_search( $val1, $arr2, true)) !== false) {
$result = $found;
} else {
// Otherwise, see if we can find a case-insensitive matching string where the element from $arr2 is at the 0th location in the one from $arr1
foreach( $arr2 as $j => $val2) {
if( stripos( $val1, $val2) === 0) {
$result = $j;
break;
}
}
}
$arr3[$i] = $result;
}
It produces your desired output array:
Array ( [0] => 3 [1] => 0 [2] => 2 [3] => 4 [4] => 5 [5] => 6 )

Try array_uintersect() with a callback function that implements your "matching" algorithm.

Looks like you need 2 foreach loops, and stripos (or mb_stripos for Unicode) for comparing.

I came up with this to account for duplicates. It returns both keys and both values so you can compare to see if it's working properly. To refine it, you can just comment out the lines setting the values you don't need. It matches all cases.
<?php
$arr1 = array('Test', 'Hello', 'World', 'Foo', 'Bar1', 'Bar');
$arr2 = array('hello', 'Else', 'World', 'Tes', 'foo', 'BaR1', 'Bar');
$matches = array();
//setup the var for the initial match
$x=0;
foreach($arr1 as $key=>$value){
$searchPhrase = '!'.$value.'!i';
//Setup the var for submatching (in case there is more than one)
$y=0;
foreach($arr2 as $key2=>$value2){
if(preg_match($searchPhrase,$value2)){
$matches[$x][$y]['key1']=$key;
$matches[$x][$y]['key2']=$key2;
$matches[$x][$y]['arr1']=$value;
$matches[$x][$y]['arr2']=$value2;
}
$y++;
}
unset($y);
$x++;
}
print_r($matches);
?>
Output looks like this:
Array
(
[1] => Array
(
[0] => Array
(
[key1] => 1
[key2] => 0
[arr1] => Hello
[arr2] => hello
)
)
[2] => Array
(
[2] => Array
(
[key1] => 2
[key2] => 2
[arr1] => World
[arr2] => World
)
)
[3] => Array
(
[4] => Array
(
[key1] => 3
[key2] => 4
[arr1] => Foo
[arr2] => foo
)
)
[4] => Array
(
[5] => Array
(
[key1] => 4
[key2] => 5
[arr1] => Bar1
[arr2] => BaR1
)
)
[5] => Array
(
[5] => Array
(
[key1] => 5
[key2] => 5
[arr1] => Bar
[arr2] => BaR1
)
[6] => Array
(
[key1] => 5
[key2] => 6
[arr1] => Bar
[arr2] => Bar
)
)
)

Related

array_merge removes my duplicates

I am adding several arrays to a $result array.
Find below my example what I am currently doing:
$res = array();
$arr1 = array("key1" => array("10","12"));
$arr2 = array("key2" => array("1","11"));
$arr3 = array("key1" => array("10","12"));
$arr4 = array("key2" => array("1","11"));
$res = array_merge($res, $arr1);
$res = array_merge($res, $arr2);
$res = array_merge($res, $arr3);
$res = array_merge($res, $arr4);
print_r($res);
// OUTPUT:
// #######
Array
(
[key1] => Array
(
[0] => 10
[1] => 12
)
[key2] => Array
(
[0] => 1
[1] => 11
)
)
However, I would like to have the following output:
Array
(
[key1] => Array
(
[0] => 10
[1] => 12
)
[key2] => Array
(
[0] => 1
[1] => 11
)
[key1] => Array
(
[0] => 10
[1] => 12
)
[key2] => Array
(
[0] => 1
[1] => 11
)
)
As you can see I would like to add the duplicated arrays to my result array.
Any suggestions why array_merge replaces the duplicated merges and how to turn this behavior of?
I appreciate your replies!
As commented the answer is no. A key is inherently unique. PHP uses the key to locate the associated value.
You can not accomplish the structure you want, but if all you want is to have all the values in one array structure you could accomplish that simply with this code:
$res = array();
$res[] = array("key1" => array("10","12"));
$res[] = array("key2" => array("1","11"));
$res[] = array("key1" => array("10","12"));
$res[] = array("key2" => array("1","11"));
var_dump($res);
// You'll see all your arrays in one structure
This was meant to be a comment but ended up being too long.
You need to understand how arrays work, this is just a brief small explanation.
Let's say you have an array:
$array = array(
'key1' => 'Hello ',
'key2' => 'World',
'key1' => 'Foo ',
'key2' => 'Bar',
);
Now we try to use it like this:
echo $array['key1'].$array['key2'];
If we can use identical keys, which one will be printed?
Hello world or Foo bar? or a mix?
The real output is Foo bar because the last two will overwrite the first ones.
Here is a more detailed (and advanced) article explaining how PHP handles arrays.

Remove the first element (key and value) from an array, turning it into another array

I want to capture the first element of an array and its value in a second array, removing it from the first.
Is there a core PHP function that does what my_function does here?
function my_function(&$array) {
$key = current(array_keys($array));
$value = $array[$key];
unset($array[$key]);
return [$key => $value];
}
$array = [
'color' => 'red',
'car' => 'ford'
'house' => 'cottage',
];
$top = my_function($array);
print_r($top);
print_r($array);
Output:
Array
(
[color] => red
)
Array
(
[car] => ford
[house] => cottage
)
If there's not a core function, is there a simpler way of achieving this behavior? IIRC, passing variables by reference is frowned upon.
Bonus question: is there a word for the combination of both key and element from an array? I feel like 'element' doesn't necessarily include the key.
edit Since there seems to be a commonly held misunderstanding, at least in PHP 7, array_shift does not do the desired behavior. It only returns the first value, not the first element:
$ cat first_element.php
<?php
$array = [
'color' => 'red',
'car' => 'ford',
'house' => 'cottage',
];
$top = array_shift($array);
print_r($top);
print_r($array);
$ php first_element.php
redArray
(
[car] => ford
[house] => cottage
)
Try this (array_splice):
$top = array_splice($array, 0, 1);
The $top will contain the first element and the $array will contain the rest of the elements.
array_splice doesn't always preserve keys, so just get the key and combine with the result of array_shift to also remove it:
$result = [key($array) => array_shift($array)];
If needed, reset the array pointer:
reset($array) && $result = [key($array) => array_shift($array)];
function my_function($array) {
$first_key = key($array);
return array($first_key =>$array[$first_key] );
}
$array = array( 'color' => 'red', 'car' => 'ford','house' => 'cottage' );
$first = my_function($array);
array_shift($array);print_r($first);print_r($array);
I made this function:
function array_extract(array &$array, $num) {
$output = array_slice($array,0, $num);
array_splice($array,0, $num);
return $output;
}
Here's what it does
$ cat test.php
<?php
$test = [234,25,45,78,56];
echo "test:\n";
print_r($test);
while( count($test) ) {
echo "extraction:\n";
print_r(array_extract($test, 2));
echo "\ntest:\n";
print_r($test);
}
$ php test.php
test:
Array
(
[0] => 234
[1] => 25
[2] => 45
[3] => 78
[4] => 56
)
extraction:
Array
(
[0] => 234
[1] => 25
)
test:
Array
(
[0] => 45
[1] => 78
[2] => 56
)
extraction:
Array
(
[0] => 45
[1] => 78
)
test:
Array
(
[0] => 56
)
extraction:
Array
(
[0] => 56
)
test:
Array
(
)
Quite simple:
$array1 = [
'color' => 'red',
'car' => 'ford'
'house' => 'cottage',
];
$array2 = array_unshift($array1);
--> result
$array2 = [
'color' => 'red'
];
$array1 = [
'car' => 'ford'
'house' => 'cottage',
];

2 arrays: keep only elements with different value

I have 2 arrays:
Array
(
[0] => Array
(
[id] => 1
[fieldname] => banana
[value] => yellow
)
)
Array
(
[0] => Array
(
[id] => 1
[fieldname] => rome
[value] => city
)
[1] => Array
(
[id] => 2
[fieldname] => bla
[value] => yes
)
)
I want to create a new array that contains only elements where "id" is different. In other words I want to get this output:
Array
(
[0] => Array
(
[id] => 2
[fieldname] => bla
[value] => yes
)
)
[id] => 2 was the only different [id] so I keep it.
Said that I've already managed to achieve my goal with an inefficient pile of foreach, if statements and temp variables. I really don't want to use a wall of code for this very small thing so I started to look for a native PHP function with no success. What's the easiest way to get the result? Is it possible that I strictly need to use a foreach with so many if?
You can use array_udiff with a function.
Computes the difference of arrays by using a callback function for
data comparison.
Returns an array containing all the values of the first array that are not
present in any of the other arguments.
The code:
// Setup two arrays as per your question
$array1 = array (
'0' => array (
'id' => '1',
'fieldname' => 'banana',
'value' => 'yellow',
)
);
$array2 = array (
'0' => array (
'id' => '1',
'fieldname' => 'rome',
'value' => 'city',
),
'1' => array (
'id' => '2',
'fieldname' => 'bla',
'value' => 'yes',
)
);
// Setup the callback comparison function
function arrayCompare($array2, $array1) {
return $array2['id'] - $array1['id'];
}
// Use array_udiff() with the two arrays and the callback function
$arrayDiff = array_udiff($array2, $array1, 'arrayCompare');
print_r($arrayDiff);
The above code returns the following:
Array (
[1] => Array (
[id] => 2
[fieldname] => bla
[value] => yes
)
)
This should do it. Not super short and it does use a temporary variable, so perhaps not what you were looking for. I've named the two arrays one and two.
$ids = array();
$result = array();
foreach ($one as $x) {
$ids[$x['id']] = 1; //This way, isset($x['id']) vill return true
}
foreach ($two as $x) {
if (!isset($ids[$x['id']])) {
$result[] = $x;
}
}
I would be surprised if there wasn't an even more compact way to do it.
EDIT: This is an alternative variant with nested for each. Not particularly short either.
$result = array();
foreach ($one as $x) {
foreach ($two as $y) {
if ($x['id'] == $y['id']) {
//A match, lets try the next $x
continue 2;
}
}
//No matching id in $two
$result[] = $x;
}

Count instances of value/key in merged arrays and set value by count

I am looking at trying to do an array_merge with these arrays but I need to be able to count how many times a particular value in the array appears and give me that data back.
Here are the original arrays
Array
(
[0] => this
[1] => that
)
Array
(
[0] => this
[1] => that
[2] => some
)
Array
(
[0] => some
[1] => hello
)
Ultimately I would like it to look like this
Array
(
[this] => 2
[that] => 2
[some] => 2
[hello] = > 1
)
That would ultimately allow me to get the key and value I need. I tried 'array_unique` in this process but realized that I may not be able to count the instances of each array that they appear since this would just simple remove them all but one.
I tried something list this
$newArray = array_count_values($mergedArray);
foreach ($newArray as $key => $value) {
echo "$key - <strong>$value</strong> <br />";
}
but I am getting results like this
Array
(
[this] => 2
[that] => 2
[some] => 2
[hello] = > 1
[this] => 3
[that] => 3
[some] => 3
[hello] = > 2
[this] => 2
[that] => 2
[some] => 2
[hello] = > 1
)
Use array_count_values():
$a1 = array(0 => 'this', 1 => 'that');
$a2 = array(0 => 'this', 1 => 'that', 2 => 'some');
$a3 = array(0 => 'some', 1 => 'hello');
// Merge arrays
$test = array_merge($a1,$a2,$a3);
// Run native function
$check = array_count_values($test);
echo '<pre>';
print_r($check);
echo '</pre>';
Gives you:
Array
(
[this] => 2
[that] => 2
[some] => 2
[hello] => 1
)
EDIT: As noted by AlpineCoder:
"This will work only in the case of input arrays using numeric (or unique) keys (since array_merge will overwrite values for the same non-integer key)."
$res = array();
foreach ($arrays as $array) {
foreach ($array as $val) {
if (isset($res[$val])) {
$res[$val]++;
} else {
$res[$val] = 1;
}
}
}
As tyteen4a03 mentioned, use nested foreach loops:
$arr1 = array('foo', 'bar');
$arr2 = array('foo', 'bar', 'baz');
$arr3 = array('baz', 'bus');
$result = array();
foreach(array($arr1, $arr2, $arr3) as $arr) {
foreach ($arr as $value) {
if (!isset($result[$value])) {
$result[$value] = 0;
}
++$result[$value];
}
}
print_r($result);
The outer foreach goes through each set of items (i.e. each array) and the inner foreach loop goes through each item in each set. If the item isn't in the $result array yet, create the key there.
Result:
Array
(
[foo] => 2
[bar] => 2
[baz] => 2
[bus] => 1
)

creating one array out of multiple arrays and concatenating unique values in a certain key

I need to loop through a bunch of arrays that I am getting back from mysql that contain a lot of duplicate entries and create one array out of several. The arrays will have one or more unique values in them so I need to keep these but some how concatenate them together into one string using commas or semicolons. So far I am not having any luck with this. Here is a phpfiddle
I need it to create a single array like this:
Array
(
[0] => Array
(
[0] => test
[1] => test
[2] => test
[3] => one
[4] => two
)
[1] => Array
(
[0] => test1
[1] => test
[2] => test
[3] => three
[4] => four
)
[2] => Array
(
[0] => test2
[1] => test
[2] => test
[3] => five, seven
[4] => six, eight
)
[3] => Array
(
[0] => test3
[1] => test
[2] => test
[3] => nine
[4] => ten
)
)
from an array like this
Array
(
[0] => Array
(
[0] => test
[1] => test
[2] => test
[3] => one
[4] => two
)
[1] => Array
(
[0] => test1
[1] => test
[2] => test
[3] => three
[4] => four
)
[2] => Array
(
[0] => test2
[1] => test
[2] => test
[3] => five
[4] => six
)
[3] => Array
(
[0] => test2
[1] => test
[2] => test
[3] => seven
[4] => eight
)
[4] => Array
(
[0] => test3
[1] => test
[2] => test
[3] => nine
[4] => ten
)
)
this is what I am trying:
for($i=0; $i < count($arrayBIG); $i++) {
if($arrayBIG[$i][0] == $arrayBIG[$i+1][0]) {
$clean[$i] = array(array_unique(array_merge($arrayBIG[$i],$arrayBIG[$i+1]), SORT_REGULAR));
}
}
Okay, so I made a new answer based on your updated post. This one was quite a bit more work than the previous version, but here's the rundown: First, I looped through the big array and for each first element, I checked to see if it also appeared in the array multiple times. If it did, I noted down each location in the array so I know which elements to "merge" and then update later.
After some looping, we can locate the "columns" of data from all of the similar arrays and merge them together by using array_unique and then imploding them into a string.
Finally, reconstruct the array, unset the original items and insert our new & improved array to the original location.
// DEFAULT ARRAY
$arrayBIG = array(
array('test', 'test', 'test', 'one', 'two'),
array('test1', 'test', 'test', 'three', 'four'),
array('test1', 'test', 'test', 'asdfasd', '443llpapos'),
array('test1', 'test', 'test', '94niwnoowi', 'inoinwoinw'),
array('test2', 'test', 'test', 'five', 'six'),
array('test2', 'test', 'test', 'seven', 'eight'),
array('test3', 'test', 'test', 'nine', 'ten')
);
// STORE THE ITEM OF EACH ARRAY INTO AN ARRAY OF ITS OWN SO WE CAN CHECK FOR DUPES
foreach ($arrayBIG AS $item_array) {
$temp_array[] = $item_array[0];
}
// COUNT THE VALUES OF THE ARRAY AND STORE ANY KEYS THAT APPEAR MORE THAN ONCE
// THESE WILL BE THE ITEMS WE TRY AND MERGE
// THIS WILL NOT BE THE NUMERIC KEY, BUT THE TEXT OF THE KEY - EX: 'test2'
foreach(array_count_values($temp_array) AS $item_count_key => $item_count_val) {
if ($item_count_val > 1) {
$dupe_key_array[] = $item_count_key;
}
}
// LOOP THROUGH THE DUPE KEYS AND FIND THEIR POSITIONS, THEN MERGE THE SIMILAR ITEMS
foreach ($dupe_key_array AS $dupe_key) {
$dupe_keys = array();
$new_array = array();
// FOR EACH MAIN ARRAY, NOTE THE ACTUAL NUMERIC LOCATION OF THE VALUE IN THE MAIN ARRAY
foreach ($arrayBIG AS $array_big_key => $array_big_val) {
// WHEN WE FIND A MATCH, ADD THE NUMERIC VALUE TO THE ARRAY
// THESE WILL BE THE ITEMS THAT WILL BE REPLACED IN THE FINAL ARRAY
if ($array_big_val[0] == $dupe_key) {
$dupe_keys[] = $array_big_key;
}
}
// FOR EACH ITEM, PULL OUT THE "COLUMN" AND MERGE THEM
for($i = 0; $i < count($array_big_val); $i++) {
$temp_array_1 = array();
// FOR EACH DUPE, GET EACH INDIVIDUAL ITEM FROM EVERY POSITION AND PUT THEM INTO A TEMP ARRAY
// THIS WILL BE THE "COLUMN" FOR EACH ARRAY.
foreach ($dupe_keys AS $dupe_keys_val) {
$temp_array_1[] = $arrayBIG[$dupe_keys_val][$i];
}
// FILTER OUT DUPES AND THEN IMPLODE IT INTO A COMMA-SEPARATED STRING
$new_array[] = implode(', ', array_unique($temp_array_1));
}
// UNSET ALL OF THE ITEMS THAT WE ARE GOING TO BE REPLACING
foreach ($dupe_keys AS $array_item_to_remove) {
unset ($arrayBIG[$array_item_to_remove]);
}
// FIND THE FIRST ITEM IN THE ARRAY WE ARE GOING TO REPLACE
// WE WILL INSERT THE NEW ARRAY AT THIS LOCATION
$first_array_item_to_replace = array_shift($dupe_keys);
// SPLICE THE MAIN ARRAY AND ADD IN OUR NEW MERGED ARRAY AT THE FIRST POSITION
array_splice($arrayBIG, $first_array_item_to_replace, 0, array($first_array_item_to_replace => $new_array));
}
I looped through your initial code and was able to just reassign the variables into a temporary array. I pulled out the same item from each element of $arrayBIG:
$arrayBIG[0][1]
$arrayBIG[1][1]
$arrayBIG[2][1] ...
Then, I did an array unique on those arrays and imploded them into a string. Finally, I added that string to the final output array. It looks like you've changed the question's code on the post since I wrote this, but the concepts should still be the same.
$arrayBIG = array( 0 => array("test", "test", "test", "one", "two"),
1 => array("test", "test", "test", "three", "four"),
2 => array("test", "test", "test", "five", "six"));
$new_array = array();
for ($j = 0; $j < count($arrayBIG[0]); $j++) {
$temp_array = array();
for ($i = 0; $i < count($arrayBIG); $i++) {
$temp_array[] = $arrayBIG[$i][$j];
}
$new_array[] = implode(', ', array_unique($temp_array));
}
That code will have $new_array outputting this:
Array
(
[0] => test
[1] => test
[2] => test
[3] => one, three, five
[4] => two, four, six
)

Categories