Remove duplicates from a multidimensional array based on multiple keys - php

Sorry if this was asked before, but I searched a lot and couldn't find a solution.
I've been trying to solve this problem for a while now, and couldn't write the function for it.
I have an array like that:
$numbers = array(
array("tag" => "developer", "group" => "grp_1", "num" => "123123"),
array("tag" => "developer", "group" => "grp_2", "num" => "111111"),
array("tag" => "student", "group" => "grp_1", "num" => "123123"),
array("tag" => "student", "group" => "grp_2", "num" => "123123"),
array("tag" => "developer", "group" => "grp_3", "num" => "111111"),
);
I need to write a function, that removes the duplicates off this array, based on multiple keys, so my function call should look something like that:
unique_by_keys($numbers, array("num","group"));
In other terms, one number can't be in the same group more than once.
After calling unique_by_keys() by array should be like that:
$numbers = array(
array("tag" => "developer", "group" => "grp_1", "num" => "123123"),
array("tag" => "developer", "group" => "grp_2", "num" => "111111"),
array("tag" => "student", "group" => "grp_2", "num" => "123123"),
array("tag" => "developer", "group" => "grp_3", "num" => "111111"),
);
I'd appreciate if you could help me find a solution, or lead me to the correct way of thinking.
Thanks!
SOLUTION:
I was able to find a solution, by writing the following function:
( I wrote it in a way that accepts many forms of $haystack arrays )
function unique_by_keys($haystack = array(), $needles = array()) {
if (!empty($haystack) && !empty($needles)) {
$_result = array();
$result = array();
$i = 0;
foreach ($haystack as $arrayObj) {
if (is_array($arrayObj)) {
$searchArray = array();
foreach ($needles as $needle) {
if (isset($arrayObj[$needle])) {
$searchArray[$needle] = $arrayObj[$needle];
}
}
if (!in_array($searchArray, $_result)) {
foreach ($arrayObj as $key => $value) {
if (in_array($key, $needles)) {
$_result[$i][$key] = $value;
}
}
$result[] = array_merge($_result[$i], $arrayObj);
}
} else {
$result[] = $arrayObj;
}
$i++;
}
return $result;
}
}
Thanks for everyone that replied!

Bhaskar's approach which assigns unique keys in the loop to remove duplicates affords a very small function for this case.
Here is a previous and unnecessarily complicated version:
function unique_by_keys($haystack=array(),$needles=array()){
// reverse order of sub-arrays to preserve lower-indexed values
foreach(array_reverse($haystack) as $row){
$result[implode('',array_intersect_key($row,array_flip($needles)))]=$row; // assign unique keys
}
ksort($result); // sort the sub-arrays by their assoc. keys
return array_values($result); // replace assoc keys with indexed keys
}
This is the best/leanest solution I can come up with:
$numbers = array(
array("tag" => "developer", "group" => "grp_1", "num" => "123123"),
array("tag" => "developer", "group" => "grp_2", "num" => "111111"),
array("tag" => "student", "group" => "grp_1", "num" => "123123"),
array("tag" => "student", "group" => "grp_2", "num" => "123123"),
array("tag" => "developer", "group" => "grp_3", "num" => "111111")
);
function unique_by_keys($haystack=array(),$needles=array()){
foreach($haystack as $row){
$key=implode('',array_intersect_key($row,array_flip($needles))); // declare unique key
if(!isset($result[$key])){$result[$key]=$row;} // save row if non-duplicate
}
return array_values($result);
}
echo "<pre>";
var_export(unique_by_keys($numbers,array("group","num")));
echo "</pre>";
Output:
array (
0 =>
array (
'tag' => 'developer',
'group' => 'grp_1',
'num' => '123123',
),
1 =>
array (
'tag' => 'developer',
'group' => 'grp_2',
'num' => '111111',
),
2 =>
array (
'tag' => 'student',
'group' => 'grp_2',
'num' => '123123',
),
3 =>
array (
'tag' => 'developer',
'group' => 'grp_3',
'num' => '111111',
),
)

$newNumbers = array();
foreach($numbers as $key=>$values){
$newkey = $values['group'].'__'.$values['num'];
$newNumbers[$newkey] = $values;
}
var_dump($newNumbers)

Code might not be efficient, but i will work for you :)
$result = unique_by_keys($numbers, array("num","group"));
echo "<pre>";
print_R($result);
function unique_by_keys($numbers, $arr){
$new_array = array();
$output = array();
foreach ($numbers as $n){
if(isset($new_array[$n[$arr[1]]]) && $new_array[$n[$arr[1]]] == $n[$arr[0]]){
continue;
}else{
$new_array[$n[$arr[1]]] = $n[$arr[0]];
$output[] = $n;
}
}
return $output;
}

Related

Assign values in array based on array keys

How to modify an array based on the value as key?
array(
array(
"name" => "BIBAR",
"cutoff" => 20220725,
"totals" => 5614
),
array(
"name" => "BIBAR",
"cutoff" => 20220810,
"totals" => 5614
),
array(
"name" => "BIBAR",
"cutoff" => 20220825,
"totals" => 5614
)
);
I tried the following but it's not working:
foreach($cutoffs as $catoff) {
$ii = 0;
$sums[$ii][$catoff] = array_filter($array, function($val){
return $val['cutoff'] === $catoff ? $val['totals'] : $val;
});
$ii++;
}
My desired array:
array(
'20221025' => array(
12345,
12343,
24442
),
'20221110' => array(
3443,
744334
)
)
I'm stuck here for hours ... Please help
IF the "name" is irrelevant, I think also the previous answer should be fine.
If this code does "not work", then your explanation might be wrong, so you need to either explain better, or give us more examples - please mind that in your example the input and output are very different - the input you gave does not match your ouput.
My code is:
$a = array(
array(
"name" => "BIBAR",
"cutoff" => 20220725,
"totals" => 5614
),
array(
"name" => "BIBAR",
"cutoff" => 20220810,
"totals" => 5614
),
array(
"name" => "BIBAR",
"cutoff" => 20220725,
"totals" => 1234
)
);
print_r($a);
echo "\n================================\n\n";
$newArr = [];
foreach ($a as $k => $vArr) {
// maybe some validation would be useful here, check if they keys exist
$newArr[$vArr['cutoff']][] = $vArr['totals'];
}
print_r($newArr);
function changeArr($data){
$new = [];
foreach ($data as $v){
$new[$v['cutoff']][] = $v['totals'];
}
return $new;
}

PHP Loop through multi-dimensional array into new array

I have a PHP array like this...
[level1] => Array
(
[random475item] => Array
(
[attr1] => tester1
[attr2] => tester2
[attr3] => tester3
)
[random455item] => Array
(
[attr1] => tester1
[attr2] => tester2
[attr3] => tester3
)
)
I am trying to get the values of the attr2 fields in a new array. I can specify a specific like this...
$newarray = array();
newarray [] = $array['level1']['random475item']['attr2'];
newarray [] = $array['level1']['random455item']['attr2'];
But is there a way to automate this as it could be 50 random items coming up and I don't want to have to keep adding them manually.
https://www.php.net/manual/en/function.array-column.php and the code below at
https://3v4l.org/8j3ae
<?php
$array = ['level1' => [
'item1' => [
'attr1' => 'test1',
'attr2' => 'test2',
'attr3' => 'test3'
],
'item2' => [
'attr1' => 'test4',
'attr2' => 'test5',
'attr3' => 'test6'
],
]];
$values = array_column($array['level1'], 'attr2');
var_dump($values);
creates
array(2) {
[0]=>
string(5) "test2"
[1]=>
string(5) "test5"
}
You can use array_map for that :
$array = ['level1' => [
'item1' => [
'attr1' => 'test1',
'attr2' => 'test2',
'attr3' => 'test3'
],
'item2' => [
'attr1' => 'test4',
'attr2' => 'test5',
'attr3' => 'test6'
],
]];
// parse every item
$values = array_map(function($item) {
// for each item, return the value 'attr2'
return $item['attr2'];
}, $array['level1']);
I have created a sandbox for you to try;
Use foreach statement will be solved your case
$newarray = array();
foreach($array['level1'] as $randomItemKey => $randomItemValue){
$newarray[] = $randomItemValue['attr2'];
}
If the values can occur at any point in the array, you can use array_walk_recursive() which will loop through all of the values (only leaf nodes) and you can check if it is an attr2 element and add it into an output array...
$out = [];
array_walk_recursive($array, function ($data, $key ) use (&$out) {
if ( $key == "attr2" ) {
$out[] = $data;
}
});

Remove from multidimensional array if has seen id before in an basic array

I would like in php to stop duplicate messages by logging msgid to a text file using something like this file_put_contents("a.txt", implode(PHP_EOL, $array1), FILE_APPEND);
and then converting it back to an array using $array1 = file("a.txt"); I would also like to delete messages from the array if they are from a set name
I know how to convert json to an array $array1 = json_decode($json, true);
Json Reply from an api that I cannot control
{
"API": "Online",
"MSG": [
{
"info": {
"name": "example"
},
"msg": "example",
"msgid": "example"
},
{
"info": {
"name": "example"
},
"msg": "example",
"msgid": "example"
}
]
}
Hi use the following code, first test it out accordingly
$uniqueMessages = unique_multidim_array($messages,'msg');
Usage : Pass the key as the 2nd parameter for which you need to check the uniqueness of array.
<?php
/* Function to handle unique assocative array */
function unique_multidim_array($array, $key) {
/* temp array to hold unique array */
$temp_array = array();
/* array to hold */
$i = 0;
/* array to hold the key for unique array */
$key_array = array();
foreach($array as $val) {
if (!in_array($val[$key], $key_array)) {
$key_array[$i] = $val[$key];
$temp_array[$i] = $val;
}
$i++;
}
return $temp_array;
}
$messages = array(
0 => array(
'info' => array(
'name' => 'example'
),
'msg' => 'example',
'msgid' => 'example'
),
1 => array(
'info' => array(
'name' => 'example 1'
),
'msg' => 'example 1',
'msgid' => 'example 1'
),
3 => array(
'info' => array(
'name' => 'example'
),
'msg' => 'example',
'msgid' => 'example'
)
);
echo '<pre>';
echo '*****************BEFORE***********************<br/>';
var_dump($messages);
echo '*****************AFTER***********************<br/>';
$uniqueMessages = unique_multidim_array($messages,'msg');
var_dump($uniqueMessages);
This works for me this is an modded function click here for original function
function RemoveElementByArray($array, $key, $seen){
foreach($array as $subKey => $subArray){
if(in_array($subArray[$key], $seen)){
unset($array[$subKey]);
}
}
return $array;
}
Example:
$array = array(
array("id" => "1", "name" => "example1"),
array("id" => "2", "name" => "example2"),
array("id" => "3", "name" => "example3"));
$SeenArray = array("1", "2");
print_r(RemoveElementByArray($array, "id", $SeenArray));
Result:
Array
(
[2] => Array
(
[id] => 3
[name] => example3
)
)

Put array elements with specific value to end of array

I have a multidimensional array like that:
$array = array(
1 => array(
"name" => 'Jon',
"year" => '2012'
),
2 => array(
"name" => 'Jack',
"year" => '1900'
),
3 => array(
"name" => 'Lisa',
"year" => '1900'
),
4 => array(
"name" => 'Ygritte',
"year" => '1929'
),
);
All the items that have a year of '1900' should be put to the end of the array. What is a lightweight solution?
Desired result:
$array = array(
1 => array(
"name" => 'Jon',
"year" => '2012'
),
2 => array(
"name" => 'Ygritte',
"year" => '1929'
),
3 => array(
"name" => 'Jack',
"year" => '1900'
),
4 => array(
"name" => 'Lisa',
"year" => '1900'
),
);
I assume you don't care about your indexes because you rearrange them. First thing, you should probably start indexes in your array from 0 and not for 1.
You can use the following code:
$array = array(
1 => array(
"name" => 'Jon',
"year" => '2012'
),
2 => array(
"name" => 'Jack',
"year" => '1900'
),
3 => array(
"name" => 'Lisa',
"year" => '1900'
),
4 => array(
"name" => 'Ygritte',
"year" => '1929'
),
);
$array = array_values($array);
for ($i=0, $c = count($array); $i<$c; ++$i) {
if ($array[$i]['year'] == '1900') {
$array[] = $array[$i];
unset($array[$i]);
}
}
$array = array_values($array);
foreach ($array as $k => $v) {
echo $k.' '.$v['name'].' '.$v['year']."<br />";
}
Result for this is:
0 Jon 2012
1 Ygritte 1929
2 Jack 1900
3 Lisa 1900
Of course if you want, you can change your keys adding 1 to each one (starting from the last element) so you have the same output as in question but I assume this one is enough for you.
The simplest approach could be something like this:
Find the element that has year 1900 (iterating the loop) then,
For example working for index 3
$tmp = $myArray['3'];
unset($myArray['3'];
$myArray['3'] = $tmp;
$tmp is a temporary variable inside the loop.
Try using SplHeap
class MyQueue extends SplHeap
{
public function compare($item1, $item2)
{
if ($item1['year'] === $item2['year']) return 0;
return ($item1['year'] == 1900) ? -1 : 0;
}
}
$queue = new MyQueue();
foreach ($array as $item) {
$queue->insert($item);
}
$queue->top();
$array = array();
while($queue->valid()){
$array[] = $queue->current();
$queue->next();
}
print_r($array);
Hope this following code will help you:
function aasort (&$array, $key) {
$sorter=array();
$ret=array();
reset($array);
foreach ($array as $ii => $va) {
$sorter[$ii]=$va[$key];
}
rsort($sorter);
foreach ($sorter as $ii => $va) {
$ret[$ii]=$array[$ii];
}
$array=$ret;
}
aasort($array,"year");
print_r($array);

Filter multidimensional array by lowest price

I'm working with an array that I'd like to filter so it only contains the lowest prices per key. So 50 would only have one unit, same with 100, and that unit would be the lowest price.
Here's an example of what I'm working with:
$units = [
50 => [
41788 => ['StdRate' => 231.0000, 'UnitName' => "NN23"],
46238 => ['StdRate' => 303.0000, 'UnitName' => "1038"],
46207 => ['StdRate' => 303.0000, 'UnitName' => "1007"]
],
100 => [
41570 => ['StdRate' => 299.0000, 'UnitName' => "HH18"],
46214 => ['StdRate' => 388.0000, 'UnitName' => "1014"]
]
];
I wanted to avoid doing this with a complicated foreach loop, so I thought an array_filter would be nice, but having a hard time wrapping my head around it. Or would a foreach be best?
$filtered = array_filter($units, function($a) {
});
Expected Output:
[
50 => [
41788 => ['StdRate' => 231.0000, 'UnitName' => "NN23"]
],
100 => [
41570 => ['StdRate' => 299.0000, 'UnitName' => "HH18"]
]
];
Here is a function that will go through your multi-dimensional array and grab the lowest units. It will retain the key of the big array (50, 100) and the key of the unit it grabbed (41788, 41570). It will not retain multiple values of the same low rate. In those cases it will return the first of the lowest value it found. Might not be exactly what you want, but it should be a good start and you can always modify it later. It uses a nested foreach to get its work done.
Hope this help you out!
function findLows($big) {
$lowUnits = array();
foreach($big as $id => $array) {
$low = false;
$prev = false;
foreach($array as $k => $a) {
if(!$low) {
$low = $k;
$prev = $a['StdRate'];
} else {
if($a['StdRate'] < $prev) {
$prev = $a['StdRate'];
$low = $k;
}
}
}
$lowUnits[$id] = array( $low => $array[$low]);
}
return $lowUnits;
}
$bigArray = array();
$bigArray[50][41788] = array('StdRate' => 231.0000, 'UnitName' => "NN23");
$bigArray[50][46238] = array('StdRate' => 303.0000, 'UnitName' => "1038");
$bigArray[50][46207] = array('StdRate' => 303.0000, 'UnitName' => "1007");
$bigArray[100][41570] = array('StdRate' => 299.0000, 'UnitName' => "HH18");
$bigArray[100][46214] = array('StdRate' => 388.0000, 'UnitName' => "1014");
$filtered = findLows($bigArray);
var_dump($filtered);
Will produce:
array(2) {
[50]=>
array(1) {
[41788]=>
array(2) {
["StdRate"]=>
float(231)
["UnitName"]=>
string(4) "NN23"
}
}
[100]=>
array(1) {
[41570]=>
array(2) {
["StdRate"]=>
float(299)
["UnitName"]=>
string(4) "HH18"
}
}
}
For an array with 1 fewer levels of depth, you could have directly iterated the data with array_filter(). Because you want to filter each set of data relating to first level entries, you just need to nest the array_filter() inside of array_map() (for a functional-style script).
I am going to isolate the StdRate values in each set and determine the lowest value by calling min() on the temporary array generated by array_column().
To pass the $min value into the filter's scope, use use().
This snippet will potentially return multiple deep subarrays for a respective set of data IF there is a tie among the lowest values in the set.
Code: (Demo)
$units = [
50 => [
41788 => ['StdRate' => 231.0000, 'UnitName' => "NN23"],
46238 => ['StdRate' => 303.0000, 'UnitName' => "1038"],
46207 => ['StdRate' => 303.0000, 'UnitName' => "1007"]
],
100 => [
41570 => ['StdRate' => 299.0000, 'UnitName' => "HH18"],
46214 => ['StdRate' => 388.0000, 'UnitName' => "1014"]
]
];
var_export(
array_map(
function($unit) {
$min = min(array_column($unit, 'StdRate'));
return array_filter(
$unit,
function($row) use ($min) {
return $row['StdRate'] == $min;
}
);
},
$units
)
);
Output:
array (
50 =>
array (
41788 =>
array (
'StdRate' => 231.0,
'UnitName' => 'NN23',
),
),
100 =>
array (
41570 =>
array (
'StdRate' => 299.0,
'UnitName' => 'HH18',
),
),
)

Categories