I'm having trouble using array_diff correctly.
I've got 2 arrays:
$arr_1 = array(
0 => array('name' => 'Day Rate 2', 'from' => 1200, 'to' => 1400),
1 => array('name' => 'Day Rate 2', 'from' => 2000, 'to' => 2000),
);
$arr_2 = array(
0 => array('name' => 'Day Rate 2', 'from' => 0, 'to' => 1000),
1 => array('name' => 'Day Rate 2', 'from' => 1200, 'to' => 1400),
2 => array('name' => 'Day Rate 3', 'from' => 2000, 'to' => 4000),
);
I want to get the values in $arr_2 that are not present in $arr_1. I want it to return this:
0 => array('name' => 'Day Rate 2', 'from' => 0, 'to' => 1000)
To compare them, I first serialized the values of each item and created these two serialized arrays, which I can use to compare, using array_diff.
foreach ($arr_1 as $key => $val) {
$arr_1_simple[$key] = serialize(array($val['from'], $val['to']));
}
foreach ($arr_2 as $key => $val) {
$arr_2_simple[$key] = serialize(array($val['from'], $val['to']));
}
Array
(
[0] => a:2:{i:0;i:1200;i:1;i:1400;}
[1] => a:2:{i:0;i:2000;i:1;i:2000;}
)
Array
(
[0] => a:2:{i:0;i:0;i:1;i:1000;}
[1] => a:2:{i:0;i:1200;i:1;i:1400;}
[2] => a:2:{i:0;i:2000;i:1;i:4000;}
)
Since a:2:{i:0;i:1200;i:1;i:1400;} and a:2:{i:0;i:2000;i:1;i:4000;} are found in both $arr_1 and $arr_2,the odd one out is a:2:{i:0;i:0;i:1;i:1000;}, which is what I thought array_diff would return.
However, the result that I'm getting is:
print_r(array_diff($arr_2_simple, $arr_1_simple));
Array
(
[0] => a:2:{i:0;i:0;i:1;i:1000;}
[2] => a:2:{i:0;i:2000;i:1;i:4000;}
)
Can anyone tell me why a:2:{i:0;i:2000;i:1;i:4000;} is getting returned? I want all the items in $arr_2 that are not in $arr_1. How do I get this?
Your value in array 1
[1] => a:2:{i:0;i:2000;i:1;i:2000;}
does not match the value in array 2
[2] => a:2:{i:0;i:2000;i:1;i:4000;}
array_diff($arr1, $arr2). Here $arr1 is array to compare from and $arr2 is array to compare against. The function returns an array containing all the entries from $arr1 that are not present $arr2. Example
Case: 1 array_diff($arr1, $arr2)
$arr1 = [1, 2, 3];
$arr2 = [1, 3, 4];
print_r(array_diff($arr1, $arr2)); //Output: [2]
Case: 2 array_diff($arr2, $arr1)
$arr1 = [1, 2, 3];
$arr2 = [1, 3, 4];
print_r(array_diff($arr2, $arr1)); //output: [4]
Related
This question already has answers here:
Sort multidimensional array based on another array [duplicate]
(3 answers)
Closed 26 days ago.
Hi I have an array that looks like this
array[
0 => array[
'id' => 1,
'name' => 'Test 1'
'classId' => 3
],
1 => array[
'id' => 1,
'name' => 'Test 1'
'classId' => 15
],
2 => array[
'id' => 1,
'name' => 'Test 1'
'classId' => 17
],
]
And I have another array that contains classIds like:
classIds = [15, 17, 3]
And I want to sort my array based on classIds
I can do a a double loop to compare it. I am just wondering is there anyother way to get it done?
Actually one loop i enough:
<?php
$order = [15, 17, 3];
$input = [
[
'id' => 1,
'name' => 'Test 1',
'classId' => 3,
],
[
'id' => 1,
'name' => 'Test 1',
'classId' => 15,
],
[
'id' => 1,
'name' => 'Test 1',
'classId' => 17,
],
];
$output = [];
array_walk($input, function($entry) use ($order, &$output) {
$output[array_search($entry['classId'], $order)] = $entry;
});
ksort($output);
print_r($output);
In case you consider array_search(...) also a "loop" (though it internally works different), that would be an alternative which produces the same output:
<?php
$order = array_flip([15, 17, 3]);
$input = array_values([
[
'id' => 1,
'name' => 'Test 1',
'classId' => 3,
],
[
'id' => 1,
'name' => 'Test 1',
'classId' => 15,
],
[
'id' => 1,
'name' => 'Test 1',
'classId' => 17,
],
]);
$output = [];
array_walk($input, function($entry, $index) use ($order, &$output) {
$output[$order[$entry['classId']]] = $entry;
});
ksort($output);
print_r($output);
The output of both approaches is:
Array
(
[0] => Array
(
[id] => 1
[name] => Test 1
[classId] => 15
)
[1] => Array
(
[id] => 1
[name] => Test 1
[classId] => 17
)
[2] => Array
(
[id] => 1
[name] => Test 1
[classId] => 3
)
)
You can sort the array directly and in-place using usort and an anonymous comparison function that retrieves the target indices from $classIds.
Given
<?php
$arr = array(
0 => array(
'id' => 1,
'name' => 'Test 1',
'classId' => 3
),
1 => array(
'id' => 1,
'name' => 'Test 1',
'classId' => 15
),
2 => array(
'id' => 1,
'name' => 'Test 1',
'classId' => 17
),
);
$classIds = [15, 17, 3];
you can sort $arr with
// Create assoc array mapping classIds to their desired sorted position.
$classIdsOrder = array_combine($classIds, range(0, count($classIds)-1));
// Sort $arr according to the 'classId' order described by $classIdsOrder
usort($arr, function ($left, $right) use ($classIdsOrder) {
return $classIdsOrder[$left['classId']] <=> $classIdsOrder[$right['classId']];
});
print_r($arr);
which outputs
Array
(
[0] => Array
(
[id] => 1
[name] => Test 1
[classId] => 15
)
[1] => Array
(
[id] => 1
[name] => Test 1
[classId] => 17
)
[2] => Array
(
[id] => 1
[name] => Test 1
[classId] => 3
)
)
Try it online!
I am using php 7.1.
I have seen that to eliminate the duplicate elements it is enough with this
array_unique($array, SORT_REGULAR);
I've also seen this work
array_map("unserialize", array_unique(array_map("serialize", $array)));
But that only deletes the elements that are duplicated from the array, I want to delete those that are duplicated but I don't want it to leave me only 1 without a duplicate, I want it to also delete that original on which it has been based to verify that it is duplicated
How could I do it?
For example i have this
$array = array(
[0] = array(
[id] => 1,
[number] => 12345,
[date] => 2022-05-09
)
[1] = array(
[id] => 2,
[number] => 123456,
[date] => 2022-05-09
)
[2] = array(
[id] => 3,
[number] => 123456,
[date] => 2022-05-09
)
[3] = array(
[id] => 3,
[number] => 123456,
[date] => 2022-05-09
)
)
How can i let it become this:?
$array = array(
[0] = array(
[id] => 1,
[number] => 12345,
[date] => 2022-05-09
)
[1] = array(
[id] => 2,
[number] => 123456,
[date] => 2022-05-09
)
)
This should be straightforward. Pluck all IDs using array_column and use array_count_values to get counts of occurrences of each ID. Then, use array_filter to filter only unique ones.
<?php
$unique_ids = array_count_values(array_column($array,'id'));
$res = array_filter($array, fn($v) => $unique_ids[$v['id']] === 1);
print_r($res);
Online Demo
Implementing the advice from How to remove values from an array if occurring more than one time?, for best time complexity, keep a lookup array of previously encountered values and their index. When a value is encountered more than once, delete the current and original row from the input array. It is perfectly safe to call unset() on an element that has already been unset(), no breakage will occur.
I have extended your input array to demonstrate that unset() will not cause trouble. I am using "array destructuring" in the foreach() to make the code more concise.
Code: (Demo)
$array = [
['id' => 1, 'number' => 12345, 'date' => '2022-05-09'],
['id' => 2, 'number' => 123456, 'date' => '2022-05-09'],
['id' => 3, 'number' => 123456, 'date' => '2022-05-09'],
['id' => 3, 'number' => 123456, 'date' => '2022-05-09'],
['id' => 4, 'number' => 123457, 'date' => '2022-05-10'],
['id' => 4, 'number' => 123458, 'date' => '2022-05-11'],
['id' => 3, 'number' => 123459, 'date' => '2022-05-12']
];
$found = [];
foreach ($array as $index => ['id' => $id]) {
if (isset($found[$id])) {
unset($array[$index], $array[$found[$id]]);
} else {
$found[$id] = $index;
}
}
var_export($array);
Output:
array (
0 =>
array (
'id' => 1,
'number' => 12345,
'date' => '2022-05-09',
),
1 =>
array (
'id' => 2,
'number' => 123456,
'date' => '2022-05-09',
),
)
I have an array with the following structure:
[0] => Array
(
[venue1] => 1
[venue2] => 2
)
[1] => Array
(
[venue1] => 3
[venue2] => 4
)
[2] => Array
(
[venue1] => 2
[venue2] => 1
)
[3] => Array
(
[venue1] => 5
[venue2] => 6
)
I need to remove the duplicate "pair of values", in this case row [0] and row [2]
I tried it with that code, but it doesn't work (and of course it's not very elegant) ;-)
foreach ( $compare_arr as $v1 )
{
$key = array_search( intval($v1[venue1]), array_column( $compare_arr, 'venue2' ) );
if ( $key <> '' ) unset($compare_arr[$key]);
}
Do you have an idea how to solve this?
Thanks a lot for your help!
Oliver
Here is an approach where an intermediate array is formed of sorted values. That you can then search for to find duplicate pairs to remove.
<?php
$venues =
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
2 =>
array (
'venue1' => 2,
'venue2' => 1,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
);
$result = $pairs = $venues;
array_walk($pairs, 'sort');
var_export($pairs);
foreach($pairs as $k => $pair) {
if(count(array_keys($pairs, $pair)) > 1) {
unset($result[$k]);
}
}
var_export($result);
Output:
array (
0 =>
array (
0 => 1,
1 => 2,
),
1 =>
array (
0 => 3,
1 => 4,
),
2 =>
array (
0 => 1,
1 => 2,
),
3 =>
array (
0 => 5,
1 => 6,
),
)array (
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
If you want to remove occurring duplicates rather than pruning out duplicates altogether, you can do an array_unique on the sorted array above and then use the remaining keys to filter the original array.
$tmp = $venues;
array_walk($tmp, 'sort');
$tmp = array_unique($tmp, SORT_REGULAR);
$result = array_intersect_key($venues, $tmp);
var_export($result);
Output:
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
You might also first loop the array creating a compound key based on the ordered keys.
Then you can filter the result only keeping arrays where the count is 2 as nothing is added because there are no duplicates.
For example
$result = [];
$compare_arr = [
["venue1" => 1, "venue2" => 2],
["venue1" => 3, "venue2" => 4],
["venue1" => 2, "venue2" => 1],
["venue1" => 5, "venue2" => 6],
];
foreach ($compare_arr as $v1) {
sort($v1);
$cKey = $v1[0] .'-'. $v1[1];
if (array_key_exists($cKey, $result)) {
$result[$cKey][] = $v1;
continue;
}
$result[$cKey] = $v1;
}
$result = array_filter($result, function($item) {
return count($item) === 2;
});
print_r($result);
Output
Array
(
[3-4] => Array
(
[0] => 3
[1] => 4
)
[5-6] => Array
(
[0] => 5
[1] => 6
)
)
You can see the compound keys are the values with a - in between. If you want to have the keys numbered from 0, you can use array_values.
Php demo
Edit
If you want to keep the first matching single pair, you can check for the compound key and if it already exists continue the loop without overwriting the existing one.
$result = [];
$compare_arr = [
["venue1" => 1, "venue2" => 2],
["venue1" => 3, "venue2" => 4],
["venue1" => 2, "venue2" => 1],
["venue1" => 5, "venue2" => 6]
];
foreach ($compare_arr as $v1) {
sort($v1);
$cKey = $v1[0] .'-'. $v1[1];
if (array_key_exists($cKey, $result)) {
continue;
}
$result[$cKey] = $v1;
}
print_r($result);
Output
Array
(
[1-2] => Array
(
[0] => 1
[1] => 2
)
[3-4] => Array
(
[0] => 3
[1] => 4
)
[5-6] => Array
(
[0] => 5
[1] => 6
)
)
Php demo
Whether you use a classic foreach() loop or functional iteration, there is no reason to iterate the input array more than once.
This snippet will appear nearly identical to TheFourthBird's answer, but I don't like the unnecessary use of continue. This snippet will ensure no that rows in the result array have 100% shared venue values (in any order). The subarray keys will also not suffer reordering; in other words the first element key will be venue1 then the second element will be venue2. Using implode() offers additional flexibility because the code won't need to be altered if the number of elements in each row changes.
$result = [];
foreach ($data as $index => $row) {
sort($row);
$key = implode('-', $row);
if (!isset($result[$key])) {
$result[$key] = $data[$index];
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
2 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
To completely remove all rows where venue values are shared, maintain a "found" array as well as a "result" array.
Code: (Demo)
$result = [];
foreach ($data as $index => $row) {
sort($row);
$key = implode('-', $row);
if (!isset($found[$key])) {
$found[$key] = true;
$result[$key] = $data[$index];
} else {
unset($result[$key]);
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'venue1' => 3,
'venue2' => 4,
),
1 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
I need to transpose a multidimensional associative array into a multidimensional indexed array sorted against and an external associative key. In the example below, I need a way to get from the 'input' to the 'expected output'.
I've tried array_match(), array_intersect() but I think I'm missing something. There must be an elegant solution to this but I cannot figure it out.
//Input
$array = array(
array('Volvo' => 22, 'BMW' => 13, 'Saab' => 5, 'Land Rover' => 11),
array('Nissan' => 10, 'Saab' => 4),
array('Land Rover' => 22, 'BMW' => 9, 'Nissan' => 2, 'Ford' => 17)
//...
);
//Expected output
$array_cars = array( // sorted list of unique car names
0 => 'BMW',
1 => 'Ford',
2 => 'Land Rover',
3 => 'Nissan',
4 => 'Saab',
5 => 'Volvo'
//...
);
$compiled_data = array( // 2D matrix, columns: $array, rows: $array_car
array(0 => 13, 2 => 9), // 'BMW'
array(2 => 17), // 'Ford'
array(0 => 11, 2 => 22), // 'Land Rover'
array(1 => 10, 2 => 2), // 'Nissan'
array(0 => 5, 1 => 4), // 'Saab'
array(1 => 22) // 'Volvo'
//...
);
Probably the simplest thing is to just iterate over all the values, sorting them into a car indexed array. You can then use ksort to sort the data:
$output = array();
foreach ($array as $key => $a) {
foreach ($a as $car => $v) {
$output[$car][$key] = $v;
}
}
ksort($output);
$array_cars = array_keys($output);
$compiled_data = array_values($output);
var_export($array_cars);
var_export($compiled_data);
Output:
array (
0 => 'BMW',
1 => 'Ford',
2 => 'Land Rover',
3 => 'Nissan',
4 => 'Saab',
5 => 'Volvo',
)
array (
0 =>
array (
0 => 13,
2 => 9,
),
1 =>
array (
2 => 17,
),
2 =>
array (
0 => 11,
2 => 22,
),
3 =>
array (
1 => 10,
2 => 2,
),
4 =>
array (
0 => 5,
1 => 4,
),
5 =>
array (
0 => 22,
),
)
Demo on 3v4l.org
I have an array:
$array = array(
'john' => 2,
'adam' => 3,
'ben' => 10,
'tim' => 1
);
I have tried all sorts of functions with PHP to achieve this array structure:
$array = array(
'tim' => 1,
'john' => 2,
'adam' => 3,
'ben' => 10
);
Where its ordered by the array values and the key/values maintained. Any ideas?
This should work using asort():
<?php
$array = array(
'john' => 2,
'adam' => 3,
'ben' => 10,
'tim' => 1,
);
asort($array, SORT_NUMERIC);
print_r($array);
?>
output:
Array
(
[tim] => 1
[john] => 2
[adam] => 3
[ben] => 10
)
Checkout the demo.
perhaps you are looking for asort() — Sort an array and maintain index association.