I need to count the same values in multidimensional array and remove duplicates
My array now
[0] => Array
(
[id] => 1
[title] => BMW
)
[1] => Array
(
[id] => 1
[title] => BMW
)
[2] => Array
(
[id] => 2
[title] => Mercedes
)
.......
Need to output
[0] => Array
(
[id] => 1
[title] => BMW
[count] => 2
)
[1] => Array
(
[id] => 2
[title] => Mercedes
[count] => 1
)
I will be very grateful for your help
The key problem is that an array(that with an id and a title) is not hashable.
So we can try to hash it, just join them all together, and make the result string as a hash key.
So, we maintain a hash table, which key is the object hash key, and the value is the result array index.
And then, For each item we travel, we can find the result array position by the help of the hash table, and then add the count.
Here's my code.
<?php
$array_in = array(
array('id'=>1, 'title'=>'BMW'),
array('id'=>1, 'title'=>'BMW'),
array('id'=>2, 'title'=>'Mercedes'),
);
$hash = array();
$array_out = array();
foreach($array_in as $item) {
$hash_key = $item['id'].'|'.$item['title'];
if(!array_key_exists($hash_key, $hash)) {
$hash[$hash_key] = sizeof($array_out);
array_push($array_out, array(
'id' => $item['id'],
'title' => $item['title'],
'count' => 0,
));
}
$array_out[$hash[$hash_key]]['count'] += 1;
}
var_dump($array_out);
In this sample I've used array_reduce to build a second array out of appropriate elements from your first array. Due to the way that I check for duplicate values, this assumes that your initial array is sorted (all the dupes are next to each other).
function dedupe($carry, $item) {
// check if array is non empty, and the last item matches this item
if (count($carry) > 0
&& $carry[count($carry) - 1][0] == $item[0]
&& $carry[count($carry) - 1][1] == $item[1]) {
// the array contains a match, increment counter
$carry[count($carry) - 1][2]++;
} else {
// there was no match, add element to array
$carry[] = array($item[0], $item[1], 1);
}
return $carry;
}
$cars = array(
array(1, "BMW"),
array(1, "BMW"),
array(2, "Mercedes")
);
$uniques = array_reduce($cars, "dedupe", array());
var_dump($uniques);
You can iterate over the array and construct the final array following this pattern:
If a key with the id of the element exists in the final array, update its counter.
If it doesn't, create a new element with its id as key.
$orig = array(
array('id' => 1, 'title' => 'BMW'),
array('id' => 1, 'title' => 'BMW'),
array('id' => 2, 'title' => 'Mercedes'),
);
$result = array();
foreach ($orig as $element) {
(isset($result[$element["id"]])) ?
$result[$element["id"]]["count"] += 1 :
$result[$element["id"]] = array("id" => $element["id"], "title" => $element["title"], "count" => 1);
}
print_r($result);
$cars = array(
array('id'=>1, 'title'=>'BMW'),
array('id'=>1, 'title'=>'BMW'),
array('id'=>2, 'title'=>'Mercedes'),
);
$serialize = array_map("serialize", $cars);
$count = array_count_values ($serialize);
$unique = array_unique($serialize);
foreach($unique as &$u)
{
$u_count = $count[$u];
$u = unserialize($u);
$u['count'] = $u_count;
}
print_r($unique);
Else if the count is not necessary you can simply follow this example:
$result = array_map("unserialize", array_unique(array_map("serialize", $array_in)));
Related
How to update an array of objects, adding the quantities if you already have the same ID, or if you have not created a new object.
I tried to explain in the code with the arrays and also with the idea of how I would like the result to be.
old Array
$a1 = [
array(
"id" => 1,
"qty" => 1
),
array(
"id" => 2,
"qty" => 1
)
];
$a2 = [
array(
"id" => 1,
"qty" => 1
)
];
$output = array_merge($a1, $a2);
echo '<pre>';
print_r($output);
echo '</pre>';
Result Error:
Array
(
[0] => Array
(
[id] => 1
[qty] => 1
)
[1] => Array
(
[id] => 2
[qty] => 1
)
[2] => Array
(
[id] => 1
[qty] => 1
)
)
What I need, in addition to if the ID does not contain, add.
Array
(
[0] => Array
(
[id] => 1
[qty] => 2
)
[1] => Array
(
[id] => 2
[qty] => 1
)
)
You can take the first array as base, then search for the key (if existing) where the product matches the id. Then either add the quantity and recalculate the price or you just add the reformatted element (id to product conversion).
$result = $a;
foreach($b as $element) {
$matchingProductIndex = array_search($element['id'], array_column($a, 'product'));
if ($matchingProductIndex !== false) {
$pricePerUnit = $result[$matchingProductIndex]['price'] / $result[$matchingProductIndex]['qty'];
$result[$matchingProductIndex]['qty'] += $element['qty'];
$result[$matchingProductIndex]['price'] = $result[$matchingProductIndex]['qty'] * $pricePerUnit;
} else {
$result[] = [
'qty' => $element['qty'],
'product' => $element['id'],
'price' => $element['price'],
];
}
}
print_r($result);
Working example.
Loop through both arrays with foreach and check the ids against each other.
https://paiza.io/projects/lnnl5HeJSFIOz_6KD6HRIw
<?php
$arr1 = [['qty' => 4, 'id' => 4],['qty' => 1,'id' => 30]];
$arr2 = [['id' => 30, 'qty' => 19],['id' => 31, 'qty' => 2]];
$arr3 = [];
foreach($arr1 as $iArr1){
$match = false;
foreach($arr2 as $iArr2){
if($iArr1['id'] === $iArr2['id']){
$arr3[] = ['id' => $iArr1['id'], 'qty' => $iArr1['qty'] + $iArr2['qty']];
$match = true;
}
}
if(!$match){
$arr3[] = $iArr1;
$arr3[] = $iArr2;
}
}
print_r($arr3);
?>
One approach could be one I more often suggested.
First lets merge $a2 with one to simplify looping over one larger collection.
If we then create a small mapping from id to its index in the result array we can update the running total of qty.
$map = [];
$result = [];
// Merge the two and do as per usual, create a mapping
// from id to index and update the qty at the corresponding index.
foreach (array_merge($a1, $a2) as $subarr) {
$id = $subarr['id'];
if (!key_exists($id, $map)) {
$index = array_push($result, $subarr) - 1;
$map[$id] = $index;
continue;
}
$result[$map[$id]]['qty'] += $subarr['qty'];
}
echo '<pre>', print_r($result, true), '</pre>';
Output:
Array
(
[0] => Array
(
[id] => 1
[qty] => 2
)
[1] => Array
(
[id] => 2
[qty] => 1
)
)
im having trouble figuring this one out, the scenario is, that there is an multidimensional array, in which there is name, id, and number in an array, what i want is to get the unique array in which array having same name should not appear, this can be done and i had done it, but i also want that the array that is returned should contain the lowest num, i hope this would help make you understand
i have
Array(
[0]=>array(
[id]=>1
[name]=>abc
[num]=>4)
[1]=>
array(
[id]=>2
[name]=>efg
[num]=>4)
[2]=>array(
[id]=>3
[name]=>abc
[num]=>2)
)
Now its a rough array representation, what i want from this is
Array(
[0]=>array(
[id]=>3
[name]=>abc
[num]=>2)
[1]=>
array(
[id]=>2
[name]=>efg
[num]=>4)
What im using:
code.php
<?php
$details = array(
0 => array("id"=>"1", "name"=>"Mike", "num"=>"9876543210"),
1 => array("id"=>"2", "name"=>"Carissa", "num"=>"08548596258"),
2 => array("id"=>"1", "name"=>"Mathew", "num"=>"784581254"),
);
function unique_multidim_array($array, $key) {
$temp_array = array();
$i = 0;
$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;
}
$details = unique_multidim_array($details,'name');
?>
This function returns me
Array(
[0]=>array(
[id]=>1
[name]=>abc
[num]=4)
[1]=>
array(
[id]=>2
[name]=>efg
[num]=>4)
You can use array_reduce:
$result = array_reduce
(
$array,
function( $carry, $item )
{
if( !isset( $carry[$item['name']] ) || $item['num'] < $carry[$item['name']]['num'] )
{
$carry[$item['name']] = $item;
}
return $carry;
},
[] // This parameter (empty array) is optional in this case
);
$result = array_values( $result );
We compare each array element with constructing returned array (initially empty): if in returned array doesn't exists an item with key = $item['name'], we add it; otherwise, if current item has num value lower than corresponding returned array item, we replace this item with current item. At the end, we use array_values to remove associative keys.
Result:
Array
(
[0] => Array
(
[id] => 3
[name] => abc
[num] => 2
)
[1] => Array
(
[id] => 2
[name] => efg
[num] => 4
)
)
Read more about array_reduce
Read more about array_values
I would do this with two loops
First, group the values by name, then number
foreach ($your_array as $value) {
$names[$value['name']][$value['num']] = $value;
}
Then iterate the result of that, sort name by key (num) and append the first element to the final result.
foreach ($names as $set_of_nums) {
ksort($set_of_nums);
$result[] = reset($set_of_nums);
}
I have considered the below example.
$details = array(
0 => array("id"=>"1", "name"=>"Mike", "num"=>"123"),
1 => array("id"=>"2", "name"=>"Carissa", "num"=>"235"),
2 => array("id"=>"3", "name"=>"Mike", "num"=>"5"),
3 => array("id"=>"4", "name"=>"Tom", "num"=>"256"),
4 => array("id"=>"5", "name"=>"Tom", "num"=>"500"),
5 => array("id"=>"6", "name"=>"Mike", "num"=>"7"),
);
function unique_multidim_array($array, $key) {
$temp_array = array();
$key_array = array();
foreach($array as $k=>$val) {
if (!in_array($val[$key], $key_array)) {
// storing the array with the key
$key_array[$k] = $val[$key];
$temp_array[$k] = $val;
} else{
foreach($key_array as $r=>$p){
//check for the array with the name
if($temp_array[$r]['name'] == $val['name']){
// compare the value
if($temp_array[$r]['num']>$val['num']){
// store the new array to the temp_array with same key of key_array
$temp_array[$r] = $val;
}
}
}
}
}
return $temp_array;
}
$details = unique_multidim_array($details,'name');
Output:
Array
(
[0] => Array
(
[id] => 3
[name] => Mike
[num] => 5
)
[1] => Array
(
[id] => 2
[name] => Carissa
[num] => 235
)
[3] => Array
(
[id] => 4
[name] => Tom
[num] => 256
)
)
You can sort the output array using any of the sort functions as you desire.
I have an array of arrays set up like so. There are a total of 10 arrays but I will just display the first 2. The second column has a unique id of between 1-10 (each only used once).
Array
(
[0] => Array
(
[0] => User1
[1] => 5
)
[1] => Array
(
[0] => User2
[1] => 3
)
)
I have another array of arrays:
Array
(
[0] => Array
(
[0] => 3
[1] => 10.00
)
[1] => Array
(
[0] => 5
[1] => 47.00
)
)
where the first column is the id and the second column is the value I want to add to the first array.
Each id (1-10) is only used once. How would I go about adding the second column from Array#2 to Array#1 matching the ID#?
There are tons of ways to do this :) This is one of them, optimizing the second array for search and walking the first one:
Live example
<?
$first_array[0][] = 'User1';
$first_array[0][] = 5;
$first_array[1][] = 'User2';
$first_array[1][] = 3;
$secnd_array[0][] = 3;
$secnd_array[0][] = 10.00;
$secnd_array[1][] = 5;
$secnd_array[1][] = 47.00;
// Make the user_id the key of the array
foreach ($secnd_array as $sca) {
$searchable_second_array[ $sca[0] ] = $sca[1];
}
// Modify the original array
array_walk($first_array, function(&$a) use ($searchable_second_array) {
// Here we find the second element of the first array in the modified second array :p
$a[] = $searchable_second_array[ $a[1] ];
});
// print_r($first_array);
Assuming that 0 will always be the key of the array and 1 will always be the value you'd like to add, a simple foreach loop is all you need.
Where $initial is the first array you provided and $add is the second:
<?php
$initial = array(array("User1", 5),
array("User2", 3));
$add = array(
array(0, 10.00),
array(1, 47.00));
foreach ($add as $item) {
if (isset($initial[$item[0]])) {
$initial[$item[0]][] = $item[1];
}
}
printf("<pre>%s</pre>", print_r($arr1[$item[0]], true));
I don't know if I got you right, but I've come up with a solution XD
<?php
$array_1 = array(
0 => array(
0 => 'ID1',
1 => 5
),
1 => array(
0 => 'ID2',
1 => 3
)
);
$array_2 = array(
0 => array(
0 => 3,
1 => 10.00
),
1 => array(
0 => 5,
1 => 47.00
)
);
foreach($array_1 as $key_1 => $arr_1){
foreach($array_2 as $key_2 => $arr_2){
if($arr_2[0] == $arr_1[1]){
$array_1[$key_1][2] = $arr_2[1];
}
}
}
var_dump($array_1);
?>
Demo: https://eval.in/201648
The short version would look like this:
<?php
$array_1 = array(array('ID1',5),array('ID2',3));
$array_2 = array(array(3,10.00),array(5,47.00));
foreach($array_1 as $key => $arr_1){
foreach($array_2 as$arr_2){
if($arr_2[0] == $arr_1[1]){
$array_1[$key][2] = $arr_2[1];
}
}
}
var_dump($array_1);
?>
Demo: https://eval.in/201649
Hope that helps :)
A quick and dirty way just to show you one of the more self-explaining ways to do it :)
$users = array(
0 => array(
0 => 'User1',
1 => 123
),
1 => array(
0 => 'User2',
1 => 456
)
);
$items = array(
0 => array(
0 => 123,
1 => 'Stuff 1'
),
1 => array(
0 => 456,
1 => 'Stuff 2'
)
);
foreach($items as $item){
foreach($users as $key => $user){
if($item[0] == $user[1])
array_push($users[$key], $item[1]);
}
}
I have a multidimensional array and am trying to group them according to the value in a specific column.
I'm trying to group them by level, but I won't actually know the level beforehand. So, it's not like I can put it in a for loop and say while $i < 7, because I won't know that 7 is the maximum value for the level key, and frankly, I'm not sure that's how I would need to do it even if I did.
[
['cust' => 'XT8900', 'type' => 'standard', 'level' => 1],
['cust' => 'XT8944', 'type' => 'standard', 'level' => 1],
['cust' => 'XT8922', 'type' => 'premier', 'level' => 3],
['cust' => 'XT8816', 'type' => 'permier', 'level' => 3],
['cust' => 'XT7434', 'type' => 'standard', 'level' => 7],
]
Desired result:
Array (
[1] => Array (
[0] => Array (
[cust] => XT8900
[type] => standard
)
[1] => Array (
[cust] => XT8944
[type] => standard
)
)
[3] => Array (
[2] => Array (
[cust] => XT8922
[type] => premier
)
[3] => Array (
[cust] => XT8816
[type] => permier
)
)
[7] => Array (
[4] => Array (
[cust] => XT7434
[type] => standard
)
)
)
Best way, if you have control over building the initial array, is just set things up like that at the start as you add entries.
If not then build a temporary array to sort:
foreach ($input_arr as $key => &$entry) {
$level_arr[$entry['level']][$key] = $entry;
}
Leaves you with the form you wanted and everything referenced together.
Build the array like that in the first place though if at all possible.
You need to group them by level first
Use foreach to loop into array check if the level is the same with the previous item then group it with that array
$templevel=0;
$newkey=0;
$grouparr[$templevel]="";
foreach ($items as $key => $val) {
if ($templevel==$val['level']){
$grouparr[$templevel][$newkey]=$val;
} else {
$grouparr[$val['level']][$newkey]=$val;
}
$newkey++;
}
print($grouparr);
The output of print($grouparr); will display like the format you hoped for
You can also try to
print($grouparr[7]);
Will display
[7] => Array (
[4] => Array (
[cust] => XT7434
[type] => standard
)
)
Or
print($grouparr[3]);
Will display
[3] => Array (
[2] => Array (
[cust] => XT8922
[type] => premier
)
[3] => Array (
[cust] => XT8816
[type] => permier
)
)
Here is the solution I landed on for an identical problem, wrapped as a function:
function arraySort($input,$sortkey){
foreach ($input as $key=>$val) $output[$val[$sortkey]][]=$val;
return $output;
}
To sort $myarray by the key named "level" just do this:
$myArray = arraySort($myArray,'level');
Or if you didn't want it as a function, just for a one time use, this would create $myNewArray from $myArray grouped by the key 'level'
foreach ($myArray as $key=>$val) $myNewArray[$val['level']][]=$val;
function group_assoc($array, $key) {
$return = array();
foreach($array as $v) {
$return[$v[$key]][] = $v;
}
return $return;
}
//Group the requests by their account_id
$account_requests = group_assoc($requests, 'account_id');
$result = array();
foreach ($yourArrayList as $data) {
$id = $data['level'];
if (isset($result[$id])) {
$result[$id][] = $data;
} else {
$result[$id] = array($data);
}
}
Best ans.
$levels = array_unique(array_column($records, 'level'));
$data = array();
foreach($records as $key => $value){
$data[$levels[array_search($value['level'],$levels )]][] = $value ;
}
print_r($data);
To generate the question's exact desured output from the sample input, pull/pop the last value from each row, use that value as the first level grouping key. Then use the original first level index as the second level key. Then push the two remaining elements into the group's subset.
Code: (Demo)
$result = [];
foreach ($array as $key => $row) {
$result[array_pop($row)][$key] = $row;
}
var_export($result);
For functional style syntax, use array_reduce(). (Demo)
var_export(
array_reduce(
array_keys($array),
function($result, $key) use ($array) {
$result[array_pop($array[$key])][$key] = $array[$key];
return $result;
}
)
);
function _group_by($array,$key,$keyName)
{
$return = array();
foreach($array as $val) {
$return[$keyName.$val[$key]][] = $val;
}
return $return;
} //end of function
I have a multidimensional array that looks like this:
Array
(
[0] => Array
(
[0] => Array
(
[id] => 3
)
[1] => Array
(
[id] => 1
)
[2] => Array
(
[id] => 2
)
[3] => Array
(
[id] => 5
)
[4] => Array
(
[id] => 4
)
)
[1] => Array
(
[0] => Array
(
[id] => 1
)
[1] => Array
(
[id] => 3
)
[2] => Array
(
[id] => 4
)
[3] => Array
(
[id] => 5
)
)
[2] => Array
(
[0] => Array
(
[id] => 3
)
)
)
I need to find a way to return the intersecting value(s). In this case that would be
[id] => 3
The length of the array could be different, so I can't just use array_intersect().
This would be simple if your arrays contained only integers, but as they contain an another array, it gets a bit more complicated. But this should do it:
function custom_intersect($arrays) {
$comp = array_shift($arrays);
$values = array();
// The other arrays are compared to the first array:
// Get all the values from the first array for comparison
foreach($comp as $k => $v) {
// Set amount of matches for value to 1.
$values[$v['id']] = 1;
}
// Loop through the other arrays
foreach($arrays as $array) {
// Loop through every value in array
foreach($array as $k => $v) {
// If the current ID exists in the compare array
if(isset($values[$v['id']])) {
// Increase the amount of matches
$values[$v['id']]++;
}
}
}
$result = array();
// The amount of matches for certain value must be
// equal to the number of arrays passed, that's how
// we know the value is present in all arrays.
$n = count($arrays) + 1;
foreach($values as $k => $v) {
if($v == $n) {
// The value was found in all arrays,
// thus it's in the intersection
$result[] = $v;
}
}
return $result;
}
Usage:
$arrays = array(
array(array('id' => 3), array('id' => 1), array('id' => 2), array('id' => 5), array('id' => 4)),
array(array('id' => 1), array('id' => 3), array('id' => 4), array('id' => 5)),
array(array('id' => 3))
);
print_r(custom_intersect($arrays));
Result:
Array
(
[0] => 3
)
This function isn't perfect: if you have duplicate ID's in one array, it will not work. That would require a bit more code to first make the array values unique, but this will probably work in your case.
You can use array_uintersect() to get the intersection of arrays using a custom comparison function. You have to call it with call_user_func_array() though, as it expects each array as a separate argument:
//build list of parameters for array_uintersect()
$params = array_merge($input, array('compare_func'));
$result = call_user_func_array('array_uintersect', $params);
function compare_func($a, $b) {
return $a['id'] - $b['id'];
}
You can't simply call array_intersect() with call_user_func_array(), because it seems to compare arrays by comparing their string representation (which will always be 'Array').
$a = array(4,3);
$b = array(4,3,2,1);
$c = array(1,2,3,4,5);
$d = array(5,4,3);
$array = array($a,$b,$c,$d);
for ($i=0; $i<count($array); $i++){
if ($i==0){
$array2 = $array[$i];
} else {
$array2 = array_intersect($array2, $array[$i]);
}
}
print_r($array2);`
Result:
3,4
As mentioned in one of the comments of the php.net website (array_intersect function).
$a = array(1,2,3,4,5,2,6,1); /* repeated elements --> $a is not a set */
$b = array(0,2,4,6,8,5,7,9,2,1); /* repeated elements --> $b is not a set */
$ua = array_merge(array_unique($a)); /* now, $a is a set */
$ub = array_merge(array_unique($b)); /* now, $b is a set */
$intersect = array_merge(array_intersect($ua,$ub));
This will return this array:
Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 5
[4] => 6
)