Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 months ago.
Improve this question
How to merge n number of array in php. I mean how can I do the job like :
array_merge(from : $result[0], to : $result[count($result)-1])
OR
array_merge_recursive(from: $result[0], to : $result[count($result) -1])
Where $result is an array with multiple arrays inside it like this :
$result = Array(
0 => array(),//associative array
1 => array(),//associative array
2 => array(),//associative array
3 => array()//associative array
)
My Result is :
$result = Array(
0 => Array(
"name" => "Name",
"events" => 1,
"types" => 2
),
1 => Array(
"name" => "Name",
"events" => 1,
"types" => 3
),
2 => Array(
"name" => "Name",
"events" => 1,
"types" => 4
),
3 => Array(
"name" => "Name",
"events" => 2,
"types" => 2
),
4 => Array(
"name" => "Name",
"events" => 3,
"types" => 2
)
)
And what I need is
$result = Array(
"name" => "name",
"events" => array(1,2,3),
"types" => array(2,3,4)
)
array_merge can take variable number of arguments, so with a little call_user_func_array trickery you can pass your $result array to it:
$merged = call_user_func_array('array_merge', $result);
This basically run like if you would have typed:
$merged = array_merge($result[0], $result[1], .... $result[n]);
Update:
Now with 5.6, we have the ... operator to unpack arrays to arguments, so you can:
$merged = array_merge(...$result);
And have the same results. *
* The same results as long you have integer keys in the unpacked array, otherwise you'll get an E_RECOVERABLE_ERROR : type 4096 -- Cannot unpack array with string keys error.
I really liked the answer from complex857 but it didn't work for me, because I had numeric keys in my arrays that I needed to preserve.
I used the + operator to preserve the keys (as suggested in PHP array_merge with numerical keys) and used array_reduce to merge the array.
So if you want to merge arrays inside an array while preserving numerical keys you can do it as follows:
<?php
$a = [
[0 => 'Test 1'],
[0 => 'Test 2', 2 => 'foo'],
[1 => 'Bar'],
];
print_r(array_reduce($a, function ($carry, $item) { return $carry + $item; }, []));
?>
Result:
Array
(
[0] => Test 1
[2] => foo
[1] => Bar
)
If you would like to:
check that each param going into array_merge is actually an array
specify a particular property within one of the arrays to merge by
You can use this function:
function mergeArrayofArrays($array, $property = null)
{
return array_reduce(
(array) $array, // make sure this is an array too, or array_reduce is mad.
function($carry, $item) use ($property) {
$mergeOnProperty = (!$property) ?
$item :
(is_array($item) ? $item[$property] : $item->$property);
return is_array($mergeOnProperty)
? array_merge($carry, $mergeOnProperty)
: $carry;
}, array()); // start the carry with empty array
}
Let's see it in action.. here's some data:
Simple structure: Pure array of arrays to merge.
$peopleByTypesSimple = [
'teachers' => [
0 => (object) ['name' => 'Ms. Jo', 'hair_color' => 'brown'],
1 => (object) ['name' => 'Mr. Bob', 'hair_color' => 'red'],
],
'students' => [
0 => (object) ['name' => 'Joey', 'hair_color' => 'blonde'],
1 => (object) ['name' => 'Anna', 'hair_color' => 'Strawberry Blonde'],
],
'parents' => [
0 => (object) ['name' => 'Mr. Howard', 'hair_color' => 'black'],
1 => (object) ['name' => 'Ms. Wendle', 'hair_color' => 'Auburn'],
],
];
Less simple: Array of arrays, but would like to specify the people and ignore the count.
$peopleByTypes = [
'teachers' => [
'count' => 2,
'people' => [
0 => (object) ['name' => 'Ms. Jo', 'hair_color' => 'brown'],
1 => (object) ['name' => 'Mr. Bob', 'hair_color' => 'red'],
]
],
'students' => [
'count' => 2,
'people' => [
0 => (object) ['name' => 'Joey', 'hair_color' => 'blonde'],
1 => (object) ['name' => 'Anna', 'hair_color' => 'Strawberry Blonde'],
]
],
'parents' => [
'count' => 2,
'people' => [
0 => (object) ['name' => 'Mr. Howard', 'hair_color' => 'black'],
1 => (object) ['name' => 'Ms. Wendle', 'hair_color' => 'Auburn'],
]
],
];
Run it
$peopleSimple = mergeArrayofArrays($peopleByTypesSimple);
$people = mergeArrayofArrays($peopleByTypes, 'people');
Results - Both return this:
Array
(
[0] => stdClass Object
(
[name] => Ms. Jo
[hair_color] => brown
)
[1] => stdClass Object
(
[name] => Mr. Bob
[hair_color] => red
)
[2] => stdClass Object
(
[name] => Joey
[hair_color] => blonde
)
[3] => stdClass Object
(
[name] => Anna
[hair_color] => Strawberry Blonde
)
[4] => stdClass Object
(
[name] => Mr. Howard
[hair_color] => black
)
[5] => stdClass Object
(
[name] => Ms. Wendle
[hair_color] => Auburn
)
)
Extra Fun:
If you want to single out one property in an array or object, like "name" from an array of people objects(or associate arrays), you can use this function
function getSinglePropFromCollection($propName, $collection, $getter = true)
{
return (empty($collection)) ? [] : array_map(function($item) use ($propName) {
return is_array($item)
? $item[$propName]
: ($getter)
? $item->{'get' . ucwords($propName)}()
: $item->{$propName}
}, $collection);
}
The getter is for possibly protected/private objects.
$namesOnly = getSinglePropFromCollection('name', $peopleResults, false);
returns
Array
(
[0] => Ms. Jo
[1] => Mr. Bob
[2] => Joey
[3] => Anna
[4] => Mr. Howard
[5] => Ms. Wendle
)
Try this
$result = array_merge($array1, $array2);
Or, instead of array_merge, you can use the + op which performs a union:
$array2 + array_fill_keys($array1, '');
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 searched a lot of SOF threads and no one seems to stick to my problem. What's kind of wired because this should be a well discussed question :)
Maybe I'm seeking for the wrong thing...
Scenario:
I have 2 arrays
$a = [
['id' => 5, 'name' => 'bruce'],
['id' => 7, 'name' => 'wayne']
];
// 2 elements
and
$b = [
['id' => 6, 'name' => 'chuck'],
['id' => 8, 'name' => 'norris'],
['id' => 7, 'name' => 'wayne'] //also exists in array $a
];
// 3 elements
My goal is
$c = [
['id' => 6, 'name' => 'chuck'],
['id' => 8, 'name' => 'norris'],
['id' => 7, 'name' => 'wayne'],
['id' => 5, 'name' => 'bruce']
];
// 4 elements (no duplicates)
I really don't care about the order inside the array(s) but I want to merge both into one, without having duplicates.
I tried array_merge and array_merge_recursive. No one works. Probably because the functions doesn't know the identifier which identifies each entry. Is there an easy solution or do I really have to create an own method/function for this?
Maybe there is a closure that I could use?
You can do this with very simple inbuilt function of PHP
$c = array_unique(array_merge($a,$b), SORT_REGULAR);
print_r( $c )
The output of the print_r is
Array
(
[0] => Array
(
[id] => 5
[name] => bruce
)
[1] => Array
(
[id] => 7
[name] => wayne
)
[2] => Array
(
[id] => 6
[name] => chuck
)
[3] => Array
(
[id] => 8
[name] => norris
)
)
$temp = array_merge($b, $a);
foreach ($temp as $v) {
$c[$v['id']] = $v;
}
If it finds the same id, the element will be overwritten in $c
Here's an approach at hashing each array with serialize after a key sort:
<?php
$a = [
['id' => 5, 'name' => 'bruce'],
['id' => 7, 'name' => 'wayne']
];
$b = [
['id' => 6, 'name' => 'chuck'],
['name' => 'wayne', 'id' => 7],
['id' => 8, 'name' => 'norris']
];
$merged = array_merge($a, $b);
foreach($merged as $k => $v) {
ksort($v);
$hashes[$k] = serialize($v);
}
$hashes = array_unique($hashes);
var_export(array_intersect_key($merged, $hashes));
Output:
array (
0 =>
array (
'id' => 5,
'name' => 'bruce',
),
1 =>
array (
'id' => 7,
'name' => 'wayne',
),
2 =>
array (
'id' => 6,
'name' => 'chuck',
),
4 =>
array (
'id' => 8,
'name' => 'norris',
),
)
If you index them on unique id then just add them. The result will be indexed on id which is convenient:
$result = array_column($a, null, 'id') + array_column($b, null, 'id');
I don't know how performant this is, but just using phps array-manipulation functions I get:
>>> array_values(array_merge(array_combine(array_column($a, 'name'), $a), array_combine(array_column($b, 'name'), $b)));
=> [
[
"id" => 5,
"name" => "bruce",
],
[
"id" => 7,
"name" => "wayne",
],
[
"id" => 6,
"name" => "chuck",
],
[
"id" => 8,
"name" => "norris",
],
]
I have an associative array key/values indicating a group. I'd like to shuffle the array so that the groups are in a random order but so that the items in the groups are shuffled only within their group. In other words, I want to take something like this:
[
[
"name" => "Buffy",
"group" => 1
],
[
"name" => "Willow",
"group" => 1
],
[
"name" => "Xander",
"group" => 2
],
[
"name" => "Giles",
"group" => 2
],
[
"name" => "Tara",
"group" => 3
],
[
"name" => "Angel",
"group" => 3
],
[
"name" => "Spike",
"group" => 3
]
]
And return something a bit more like this:
[
[
"name" => "Spike",
"group" => 3
]
[
"name" => "Angel",
"group" => 3
],
[
"name" => "Tara",
"group" => 3
],
[
"name" => "Willow",
"group" => 1
],
[
"name" => "Buffy",
"group" => 1
],
[
"name" => "Xander",
"group" => 2
],
[
"name" => "Giles",
"group" => 2
],
]
I realise this is probably possible with two or three sorts, but it would be great if this could be done with a single usort.
Basically, my solution shuffles the input array, temporarily restructures the input array to gather subarrays with the same group value, then returns the data to its original structure.
Code: (Demo)
shuffle($data); // randomize all subarrays
foreach ($data as $set) {
$grouped[$set['group']][] = $set; // merge subarrays on group value
}
$output = [];
foreach($grouped as $group) {
array_push($output, ...$group); // return to original array structure
}
var_export($output);
*Note that ... (splat operator) allows array_push() to store all subarrays (within each group) individually to generate the original structure.
Possible Output:
array (
0 =>
array (
'name' => 'Xander',
'group' => 2,
),
1 =>
array (
'name' => 'Giles',
'group' => 2,
),
2 =>
array (
'name' => 'Willow',
'group' => 1,
),
3 =>
array (
'name' => 'Buffy',
'group' => 1,
),
4 =>
array (
'name' => 'Spike',
'group' => 3,
),
5 =>
array (
'name' => 'Tara',
'group' => 3,
),
6 =>
array (
'name' => 'Angel',
'group' => 3,
),
)
Try...
$groups = [];
foreach (array_unique(array_column($myar, 'group')) as $k) $groups[$k] = rand();
foreach (array_keys($myar) as $k) $myar[$k]['rnd'] = rand();
usort($myar, function($a, $b) use ($groups) {
if ($gdif = $groups[$b['group']] - $groups[$a['group']]) return $gdif;
return $b['rnd'] - $a['rnd'];
});
foreach (array_keys($myar) as $k) unset($myar[$k]['rnd']);
print_r($myar);
Run with your data, this is one result...
Array
(
[0] => Array
(
[name] => Buffy
[group] => 1
)
[1] => Array
(
[name] => Willow
[group] => 1
)
[2] => Array
(
[name] => Angel
[group] => 3
)
[3] => Array
(
[name] => Tara
[group] => 3
)
[4] => Array
(
[name] => Spike
[group] => 3
)
[5] => Array
(
[name] => Giles
[group] => 2
)
[6] => Array
(
[name] => Xander
[group] => 2
)
)
I am comparing three value in an array and i am getting all value.
How can i output specific value or value that i only want to output
Because i want to output value that i only nedeed.
I have this code:
<?php
$participants = [
[ 'calleridnum' => 1,
'callee' => 'yay'
],
[ 'calleridnum' => 2,
'callee' => 'yay'
],
[ 'calleridnum' => 3,
'callee' => 'yay'
]
];
$conferance_participants = [
[ 'uid' => 1,
'caller' => 'yay2',
'dit' => 'deze'
],
[ 'uid' => 2,
'caller' => 'test',
'dit' => 'wew'
]
];
$contacts = [
[ 'name' => 1,
'test' => 'yay2',
'limit' => 1
],
[ 'name' => 2,
'test' => 'yay2',
'limit' => 1
]
];
foreach ($participants as $participant=>$p) {
foreach ($conferance_participants as $conferance_participant=>$cp) {
foreach ($contacts as $contact=>$cs) {
if (($p['calleridnum'] == $cp['uid']) && ($cp['uid'] == $cs['name'])){
$conferance_participants[$conferance_participant] = array_merge(
$participants[$participant],
$conferance_participants[$conferance_participant],
$contacts[$contact]
);
}
}
}
}
echo "<pre>";
print_r($conferance_participants);
echo "</pre>";
?>
and my output is:
Array
(
[0] => Array
(
[calleridnum] => 1
[callee] => yay
[uid] => 1
[caller] => yay2
[dit] => deze
[name] => 1
[test] => yay2
[limit] => 1
)
[1] => Array
(
[calleridnum] => 2
[callee] => yay
[uid] => 2
[caller] => test
[dit] => wew
[name] => 2
[test] => yay2
[limit] => 1
)
)
I want ot minimize my output.
I want to remove name test from the $contacts array
I also want to remove caller dit from the $conferance_participants array
so that my output will be :
Array
(
[0] => Array
(
[calleridnum] => 1
[callee] => yay
[uid] => 1
[limit] => 1
)
[1] => Array
(
[calleridnum] => 2
[callee] => yay
[uid] => 2
[limit] => 1
)
)
Your code is hard to understand,
Number of times your foreach will execute,
count($participants) * count($conferance_participants) * count($contacts);
Number of times this code's foreach will execute will be equal or less than your codes, because it will stop as soon as the match found.
Also i have created a new function, for searching in another arrays, so it will make the next person working on this code less bang his head on the desk.
Passes $conferance_participants variable's value as reference, note the & in foreach declaration so no need to worry about keys of the array.
foreach($conferance_participants as &$record) {
# find keys of corresponding array matches
$key_in_participants = _custom_search($record['uid'], $participants, 'calleridnum');
$key_in_contacts = _custom_search($record['uid'], $contacts, 'name');
# unset unwanted things
unset($record['caller'], $record['dit']);
# activate this code if you want to make false for unmatched records
/* ***********************************************************************
if($key_in_participants === false || $key_in_contacts === false) {
$record['calleridnum'] = $record['callee'] = $record['limit'] = false;
continue;
}
*********************************************************************** */
# setting required things
$record['calleridnum'] = $participants[$key_in_participants]['calleridnum'];
$record['callee'] = $participants[$key_in_participants]['callee'];
$record['limit'] = $contacts[$key_in_contacts]['limit'];
}
function _custom_search($id, $array, $key_to_search) {
foreach ($array as $key => $val) if($val[$key_to_search] === $id) return $key;
return false;
}
This will make $conferance_participants exactly as you want.
You can just unset the array keys before you merge the arrays. Then set the 'name' key again for $contacts array which is needed for the loops above.
Here is the modified code sample:
<?php
$participants = [
[ 'calleridnum' => 1,
'callee' => 'yay'
],
[ 'calleridnum' => 2,
'callee' => 'yay'
],
[ 'calleridnum' => 3,
'callee' => 'yay'
]
];
$conferance_participants = [
[ 'uid' => 1,
'caller' => 'yay2',
'dit' => 'deze'
],
[ 'uid' => 2,
'caller' => 'test',
'dit' => 'wew'
]
];
$contacts = [
[ 'name' => 1,
'test' => 'yay2',
'limit' => 1
],
[ 'name' => 2,
'test' => 'yay2',
'limit' => 1
]
];
foreach ($participants as $participant=>$p) {
foreach ($conferance_participants as $conferance_participant=>$cp) {
foreach ($contacts as $contact=>$cs) {
if (($p['calleridnum'] == $cp['uid']) && ($cp['uid'] == $cs['name'])){
unset($contacts[$contact]['name'], $contacts[$contact]['test']);
unset($conferance_participants[$conferance_participant]['caller'], $conferance_participants[$conferance_participant]['dit']);
$conferance_participants[$conferance_participant] = array_merge(
$participants[$participant],
$conferance_participants[$conferance_participant],
$contacts[$contact]
);
$contacts[$contact]['name'] = $cs['name'];
}
}
}
}
echo "<pre>";
print_r($conferance_participants);
echo "</pre>";
The output you should get:
Array
(
[0] => Array
(
[calleridnum] => 1
[callee] => yay
[uid] => 1
[limit] => 1
)
[1] => Array
(
[calleridnum] => 2
[callee] => yay
[uid] => 2
[limit] => 1
)
)
I hope that answers your question. Thanks.
After merging, you may filter only certain keys you want to be present in final result using array_intersect_key().
$keys = array_flip(['calleridnum', 'callee', 'uid', 'limit']);
$conferance_participants[$conferance_participant] =
array_intersect_key(
array_merge(
$participants[$participant],
$conferance_participants[$conferance_participant],
$contacts[$contact]
),
$keys
);