Sort multidimensional array by column value within a column - php

I have an array in PHP and I need to sort by a nested array inside of the array...
Here is my array:
Array
(
[0] => Array
(
[project_id] => 1
[earnest_money_due] => Array
(
[value] => 1000.00,
[currency] => 'USD'
)
)
[1] => Array
(
[project_id] => 2
[earnest_money_due] => Array
(
[value] => 200.00,
[currency] => 'USD'
)
)
[2] => Array
(
[project_id] => 3
[earnest_money_due] => Array
(
[value] => 900.00,
[currency] => 'USD'
)
)
Here's how I'm trying to sort it:
$records - this the array of records
$column - this is the the sortable column "earnest_money_due"
$columns = array_column($records, $column);
array_multisort($columns, SORT_ASC, $records);
I need to be able to sort by the [value] of the [earnest_money_due]. My code doesn't work because it's trying to sort an array, not a value.

Try this...
<?php
$array = [
[
'project_id' => 1,
'earnest_money_due' => [
'value' => 1000.00,
'currency' => 'USD',
],
],
[
'project_id' => 2,
'earnest_money_due' => [
'value' => 200.00,
'currency' => 'USD',
],
],
[
'project_id' => 3,
'earnest_money_due' => [
'value' => 900.00,
'currency' => 'USD',
],
],
];
array_multisort(
array_map(
static function ($element) {
return $element['earnest_money_due']['value'];
},
$array
),
SORT_ASC,
$array
);
var_dump($array);

If you remove the SORT_ASC, then your code works just fine. This is because PHP will sort your subarray as expected with it removed. It will compare from the start of the subarray. (Demo)
array_multisort(array_column($array, 'earnest_money_due'), $array);
If that seems too hacky, unreliable, or unintuitive, array_map() is fine. (Demo)
array_multisort(array_map(fn($row) => $row['earnest_money_due']['value'], $array), $array);
There is also nothing wrong with using usort(). (Demo)
usort($array, fn($a, $b) => $a['earnest_money_due']['value'] <=> $b['earnest_money_due']['value']);
Regardless of which array_multsort() technique you use, you don't need to explicitly use SORT_ASC because this is the default sorting order.

Related

How to remove from a multidimensional array all duplicate elements including the original?

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',
),
)

Turn each array item into key value pair

I have two arrays. One is a list of colors, the second is an associative array of key value pairs. My objective is to take the key value pairs from the associative array and make them a sub-array of each item in the colors array. Searching SO has gotten me various adjacent issues, but not the one I'm having specifically. Here are two example arrays, and then $final is what I want to achieve:
$colors = ['#eea845', '#64A0B4', '#003c50', '#FF5568', '#eee', '#5cb85c', '#5bc0de', '#f0ad4e', '#d9534f'];
$test = [
'key1' => 'val1',
'key2' => 'val2',
'key3' => 'val3',
'key4' => 'val4',
'key5' => 'val5',
'key6' => 'val6',
'key7' => 'val7',
'key8' => 'val8',
'key9' => 'val9',
];
$final = [
'#eea845' => [
'name' => 'key1',
'value' => 'val1',
],
'#64A0B4' => [
'name' => 'key2',
'value' => 'val2',
],
etc.....
]
I've been looking at array_walk, array_map, and trying to figure out how to combine for and foreach loops. I looked at the answer given here (Append one array values as key value pair to another array php) but I'm not sure how to use it on an already existing array and be able to get the index of each. For example, that solution uses array_walk($array1, function(&$v, $k) use($array2) { $v['date'] = $array2[$k]; }); but I need to have the values from $array2 be added to already existing items in $array1, and while I tried doing function($i, $v, $k) with $i being the index inside $array1, that didn't work, $i being undefined.
I'm stumped and not sure where to look next. How would you
return $colors[$i] =>
[
'name' => $test[$key],
'value' => $test[$name]
]
?
(For context, I am using this to get values to input into a Twig template, and this looks like the best way to do it for that half of the problem. But if it's too difficult on this side...)
There are several ways to do it. One way is to use foreach on the associative array, so you get key and value in separate variables, and to use next() to get the corresponding value from the first (indexed) array:
foreach ($test as $key => $value) {
$final[current($colors)] = ["name" => $key, "value" => $value];
next($colors);
}
In the rare event you had already used next() on $colors, you'll have to call reset($colors) before starting this loop.
I'd just use a combination of foreach() using the below logic:
Check if both the arrays have same length.
Use foreach to walk through the array and fill in the final array.
I am not good in this point, but I use a $count to keep track of the index. You can use cursors like next($colors) and current($colors), but I am not sure.
<?php
$colors = [
'#eea845', '#64A0B4', '#003c50', '#FF5568', '#eee', '#5cb85c', '#5bc0de', '#f0ad4e', '#d9534f'
];
$test = [
'key1' => 'val1',
'key2' => 'val2',
'key3' => 'val3',
'key4' => 'val4',
'key5' => 'val5',
'key6' => 'val6',
'key7' => 'val7',
'key8' => 'val8',
'key9' => 'val9',
];
if (count($colors) == count($test)) {
$count = 0;
$finalOne = array();
foreach ($test as $key => $value) {
$finalOne[$colors[$count]] = [
"name" => $key,
"value" => $value
];
$count++;
}
print_r($finalOne);
} else {
echo "Arrays are of not same length.";
}
Output
Array
(
[#eea845] => Array
(
[name] => key1
[value] => val1
)
[#64A0B4] => Array
(
[name] => key2
[value] => val2
)
[#003c50] => Array
(
[name] => key3
[value] => val3
)
[#FF5568] => Array
(
[name] => key4
[value] => val4
)
[#eee] => Array
(
[name] => key5
[value] => val5
)
[#5cb85c] => Array
(
[name] => key6
[value] => val6
)
[#5bc0de] => Array
(
[name] => key7
[value] => val7
)
[#f0ad4e] => Array
(
[name] => key8
[value] => val8
)
[#d9534f] => Array
(
[name] => key9
[value] => val9
)
)
Working Demo: http://sandbox.onlinephpfunctions.com/code/73fb63fab8ccbdb6f73c795572ae51a0d98acdb4

How to copy items to existing array

I'm new in PHP. I have reviewed some other threads about the array combination but I couldn't found the answer.
For example the first array
array (
'date' => '01.06.2019',
'day' => 'Saturday',
)
Second array
array (
0 =>
array (
'id' => 10,
'name' => 'Mj phooi',
),
)
Expected result
array (
'date' => '01.06.2019',
'day' => 'Saturday',
'id' => 10,
'name' => 'Mj phooi',
)
I did try the array_combine and array_merge_recursive but it doesn't work. The closure result I tried is like $result = array_merge($arr,$temp);. It combined two arrays but there have two arrays inside which not match with the expected result.
You are almost there, you could use array_merge but the array in the $temp is a nested array so you might take the first index 0:
$arr = [
'date' => '01.06.2019',
'day' => 'Saturday',
];
$temp = [
[
'id' => 10,
'name' => 'Mj phooi',
]
];
print_r(array_merge($arr, $temp[0]));
Result:
Array
(
[date] => 01.06.2019
[day] => Saturday
[id] => 10
[name] => Mj phooi
)
See a php demo
Note that an array can not have duplicate keys so if they have the same keys the last will override the existing key.

How to combine 2 arrays that have the same index and value with php? [duplicate]

This question already has answers here:
Merge arrays of associative arrays by shared column values [duplicate]
(3 answers)
Closed 5 months ago.
I have 2 array like follows:
$array1 = [
'0' => [
'no_invoice' => 'INV0001',
'product_code' => '1111111',
],
'1' => [
'no_invoice' => 'INV0001',
'product_code' => '1111112',
]
];
$array2 = [
'0' => [
'product_code' => '1111112',
'free_valie' => 839,
'count' => 1240
],
];
Is it possible to combine arrays above to be like this:
Array(
[0] => Array
(
'no_invoice' => 'INV0001',
'product_code' => '1111111',
)
[1] => Array
(
'no_invoice' => 'INV0001',
'product_code' => '1111112',
'free_valie' => 839,
'count' => 1240
)
)
So, if array have same product code, then it will join like the example above.
I have been tried with use array merge, array_merge($array1, $array2);
But the result is like this:
Array(
[0] => Array
(
'no_invoice' => 'INV0001',
'product_code' => '1111111',
)
[1] => Array
(
'no_invoice' => 'INV0001',
'product_code' => '1111112',
)
[2] => Array
(
'product_code' => '1111112',
'free_valie' => 839,
'count' => 1240
)
)
This code will do what you want. It loops over each value in $array1, using array_search to see if the entrie's product_code is also present in $array2 (by looking through the product_code column of $array2 extracted using array_column). If it is, the values are merged. Note that we use &$val in the foreach, causing the value to be passed by reference which allows it to be modified in the loop
foreach ($array1 as &$val) {
if (($k = array_search($val['product_code'], array_column($array2, 'product_code'))) !== false) {
$val = array_merge($val, $array2[$k]);
}
}
print_r($array1);
Output:
Array
(
[0] => Array
(
[no_invoice] => INV0001
[product_code] => 1111111
)
[1] => Array
(
[no_invoice] => INV0001
[product_code] => 1111112
[free_valie] => 839
[count] => 1240
)
)
Demo on 3v4l.org
Try below one.
$array1 = [
'0' => [
'no_invoice' => 'INV0001',
'product_code' => '1111111',
],
'1' => [
'no_invoice' => 'INV0001',
'product_code' => '1111112',
]
];
$array2 = [
'0' => [
'product_code' => '1111112',
'free_valie' => 839,
'count' => 1240
],
];
foreach ($array1 as $key => &$value) {
$key = array_search($value['product_code'], array_column($array2, 'product_code'));
if ($key !== false) {
$value = array_merge($value, $array2[$key]);
unset($array2[$key]);
$array2 = array_values($array2);
}
}
echo '<pre>';
print_r($array1);
exit;

Sort a multidimensional array by integer inside of a string value which is two levels down

I have a multidimensional array in php:
Array (
[0] => Array (
[certificate_name] => track.site
[domains] => track.site
[expiry_date] => Array (
[date] => 2018-09-25
[time] => 10:11:58
[count] => (22)
)
)
[1] => Array (
[certificate_name] => stats.com
[domains] => stats.com
[expiry_date] => Array (
[date] => 2018-09-24
[time] => 10:11:58
[count] => (43)
)
)
)
I want to sort this multidimensional array by $array['expiry_date']['count']
This can be done with usort:
$data = [
[
'certificate_name' => 'track.site',
'domains' => 'track.site',
'expiry_date' => [
'date' => '2018-09-25',
'time' => '10:11:58',
'count' => 22,
]
],
[
'certificate_name' => 'stats.com',
'domains' => 'stats.com',
'expiry_date' => [
'date' => '2018-09-24',
'time' => '10:11:58',
'count' => 43,
]
]
];
function compare_by_expiry_date_count($a, $b) {
return $a["expiry_date"]['count'] > $b["expiry_date"]['count'];
}
usort($data, "compare_by_expiry_date_count");
var_dump($data);
If I don't misunderstood your question then you need sorting by count not filtering. Also use trim() to remove parenthesis from the count value. Hope it helps :)
Try like this way, $b-$a is for desc, $a-$b is for asc
<?php
// this $b-$a is for desc, for asc try $a-$b
function sorting_by_count($a, $b)
{
return trim($b['expiry_date']['count'],'()') - trim($a['expiry_date']['count'],'()');
}
$array = [
[
'certificate_name' => 'track.site',
'domains' => 'track.site',
'expiry_date' => [
'date' => '2018-09-25',
'time' => '10:11:58',
'count' => '(22)',
]
],
[
'certificate_name' => 'stats.com',
'domains' => 'stats.com',
'expiry_date' => [
'date' => '2018-09-24',
'time' => '10:11:58',
'count' => '(43)',
]
]
];
usort($array, 'sorting_by_count');
print_r($array);
?>
DEMO: https://3v4l.org/vtRIu
For best time complexity, loop through your rows and populate a flat array containing the count values after trimming the parentheses and casting to an integer.
Then call array_multisort() with the counts array as the first argument and the original array as the second argument.
Code: (Demo)
foreach ($array as $row) {
$counts[] = (int) trim($row['expiry_date']['count'], '()');
}
array_multisort($counts, $array);
var_export($array);
Using usort() and calling trim() on every iteration will be more expensive because it will have to re-trim values that were already encountered.

Categories