So I have a result from a form post that looks like this:
$data = [
'id_1' => [
'0' => 1,
'1' => 2
],
'id_2' => [
'0' => 3,
'1' => 4
],
'id_3' => [
'0' => 5,
'1' => 6
]
];
What I want to achieve is to split this array into two different arrays like this:
$item_1 = [
'id_1' => 1,
'id_2' => 3,
'id_3' => 5
]
$item_2 = [
'id_1' => 2,
'id_2' => 4,
'id_3' => 6
]
I've tried using all of the proper array methods such as array_chunk, array_merge with loops but I can't seem to get my mind wrapped around how to achieve this. I've seen a lot of similar posts where the first keys doesn't have names like my array does (id_1, id_2, id_3). But in my case the names of the keys are crucial since they need to be set as the names of the keys in the individual arrays.
Much shorter than this will be hard to find:
$item1 = array_map('reset', $data);
$item2 = array_map('end', $data);
Explanation
array_map expects a callback function as its first argument. In the first line this is reset, so reset will be called on every element of $data, effectively taking the first element values of the sub arrays. array_map combines these results in a new array, keeping the original keys.
The second line does the same, but with the function end, which effectively grabs the last element's values of the sub-arrays.
The fact that both reset and end move the internal array pointer, is of no concern. The only thing that matters here is that they also return the value of the element where they put that pointer to.
Solution without loop and just for fun:
$result = [[], []];
$keys = array_keys($data);
array_map(function($item) use(&$result, &$keys) {
$key = array_shift($keys);
$result[0][$key] = $item[0];
$result[1][$key] = $item[1];
}, $data);
Just a normal foreach loop will do.
$item_1 = [];
$item_2 = [];
foreach ($data as $k => $v){
$item_1[$k] = $v[0];
$item_2[$k] = $v[1];
}
Hope this helps.
Related
There are two arrays:
$arr1 = [
"Value1",
"Value2",
"Value1"
];
$arr2 = [
["key_1" => "5", "key_2" => "10"], // relates to Value1
["key_1" => "2", "key_2" => "4"], // relates to Value2
["key_1" => "50", "key_2" => "100"] // relates to Value1
];
I cannot simply combine the two arrays because the duplicated values in $arr1 will lead to overwritten data from $arr2.
The behavior that I need is for subarray data to be added if a value from $arr1 is encountered more than once.
I tried to find all sorts of folding options while searching the web, but I could find anything that was right.
I need this output from the sample input arrays:
array (
'Value1' =>
array (
'key_1' => 55,
'key_2' => 110,
),
'Value2' =>
array (
'key_1' => '2',
'key_2' => '4',
),
)
I've tried to write a solution, but I'm not really sure how to tackle the problem.
foreach ($items as $item) {
if (isset($bal[$item['bonus_name']])) {
//Here I don't know how to sum a new one to the repetition?
} else {
$bal[$item['bonus_name']] = $item['bonus_count'];
}
}
Whatever I try, there's no way to sum a repetitive array of elements. I need some help.
Loop the first array to declare the index -> group relationship.
Check if the currently encountered group is unique to the output array. If so, push the entire subarray from the corresponding index in the second array into the output array as the initial values of the group.
If the group is encountered more than once, add each column value to the related amount in the group's subarray.
Code: (Demo)
$result = [];
foreach ($arr1 as $index => $group) {
if (!isset($result[$group])) {
$result[$group] = $arr2[$index];
} else {
foreach ($arr2[$index] as $key => $value) {
$result[$group][$key] += $value;
}
}
}
var_export($result);
Both of my arrays have over 500 000 elements.
I would like to return only those elements from multidimensional array that ARE NOT PRESENT in indexed array.
Here is what my multidimensional array looks like:
$new_codes = [
0 => [
'id' => 1,
'code' => 'code1',
... another values
],
1 => [
'id' => 2,
'code' => 'code2',
... another values
],
2 => [
'id' => 3,
'code' => 'code3',
... another values
]
];
Another array is just plain indexed array with code values:
$old_codes = [
'code1',
'code2',
];
For this limited example, after filtering, $new_codes should only have index 2 because that value doesn't exist in $old_codes array.
I've tried using the code bellow, but because the arrays are so huge, the operation takes so long that I thought that I somehow created infinite loop, but it seems that checking if the values for over 500000 elements exist in another array that also has over half a million elements takes very long time.
// option 1
$new = array_filter($new_codes, function ($var) use ($old_codes) {
return !in_array($var['code'], $old_codes);
});
// option 2
$filtered = [];
foreach($new_codes as $code) {
if(in_array($code['code']){
continue;
}
$filtered[] = $code;
}
Any suggestions for more optimized solutions are welcome.
Thanks in advance.
Reading this question I realized that using isset() is much better option for handling such a large amount of elements so I did this:
// switch array keys and values
$old_array = array_flip($old_array);
foreach($new_codes as $code) {
if(isset($old_array[$code['code']])){
continue;
}
$filtered[] = $code;
}
Doing this reduced the time to just few seconds.
I need to push a new property into my array of arrays of objects using the values from a flat array.
My sample arrays:
$users = [
[
(object) ["user_id" => 2]
],
[
(object) ["user_id" => 1],
(object) ["user_id" => 1],
],
[
(object) ["user_id" => 2],
(object) ["user_id" => 2]
]
];
$is_admin = [
false,
true,
true,
false,
false
];
I need to write is_admin => [boolean value] (one at a time) into each object using the values from the second array.
Desired result:
[
[
(object) ["user_id" => 2, "is_admin" => false]
],
[
(object) ["user_id" => 1, "is_admin" => true],
(object) ["user_id" => 1, "is_admin" => true],
],
[
(object) ["user_id" => 2, "is_admin" => false],
(object) ["user_id" => 2, "is_admin" => false]
]
]
I do not know how to map the second array with the first array's structure. I have tried using array_merge, but it doesn't work properly with my data structure and requirements.
You can use array_walk_recursive function:
array_walk_recursive($users, function (&$user) use (&$is_admin) {
$user->is_admin = current($is_admin);
next($is_admin);
});
Pay attention that both $user and $is_admin passed by reference.
Also, you can use outer index variable (i.e. $i) to track $is_admin position. In such case, you need to pass (to be more precise enclose) $i along side with $is_admin, only $i will be passed by reference and $is_admin by value. I decided to take advantage of current and next functions, not introducing one more variable.
Here is working demo.
One important thing. If your $is_admin array has fewer elements than $users arrays (they are leaves actually) false value will be assigned to the is_admin field of those users that comes after the point when there are no elements left in $is_admin.
I don't think you can achieve that because the arrays are very different. Since they're of different size the array_merge won't work properly. Plus, the first array is multidimensional while the 2nd array is single-dimensional.
If you are 100% certain about the mapping between the indexes of array a and b you can create a loop as follows, assuming that $array1 is the multidimensional array and $array2 is the flat array containing true and false:
$index = 0;
$final = array_map(function ($top_level_entry) {
return array_map(function ($item) {
global $index, $array2;
$item['is_admin'] = $array2[$index];
$index++;
return $item;
}, $top_level_entry);
}, $array1);
or in a simpler format:
$index = 0;
$final = [];
foreach ($array1 as $i => $arr) {
foreach($arr as $j => $user) {
$user['is_admin'] = $array2[$index];
$index++;
$arr[$j] = $user;
}
$final[$i] = $arr;
}
As an exercise in enjoying reference variables, I'll demonstrate assigning deep reference variables within a nested loop then looping over the $is_admin array to apply the values to those variables.
Admittedly, this does more loops than the other answers, but it should be said that it doesn't make any iterated function calls, does not need to maintain an incremented counter, and will not suffer if there are fewer values than reference variables (not that that is a problem with the asker's question).
Code: (Demo)
foreach ($users as &$groups) {
foreach ($groups as $obj) {
$obj->is_admin = &$refs[];
}
}
foreach ($is_admin as $i => $bool) {
$refs[$i] = $bool;
}
var_export($users);
Because objects in PHP have a default behavior of being modifiable by reference, you will notice that $obj in the nested loop is not prefixed with & -- it could have been for consistency, but it was not necessary.
As mentioned above the snippet, if there aren't enough elements in $is_admin to cover all of the reference variables declared in $users, then they will have a default value of null.
I have three arrays, say multiarray, valsarray, and otherarray. otherarray is a multidimensional array that supplies values to multiarray and valsarray, but besides that it is unimportant here. valsarray takes values from a subarray of each value in otherarray and multiarray takes straight values from otherarray, as demonstrated below:
foreach($otherarray as $other){
foreach($other as $sub){
$valsarray[] = $sub
}
$multiarray[] = array('Val1' => $other['Val1'], 'Val2' => $other['Val2']);
}
Now what I would like to do is append each key/value pair in valsarray to the current array entry of multiarray, to achieve a result similar to:
$multiarray = array('Val1' => $other['Val1'], 'Val2' => $other['Val2'],
'VALSARRAY_KEY1' => VALSARRAY_VALUE1, ..., 'VALSARRAY_KEYN' => VALSARRAY_VALUEN)
I have attempted to solve this using current in the following fashion:
foreach($valsarray as $key => $val){
current($multiarray)[$key] = $val;
}
But the multiarray remained unaltered. I may be misunderstanding how current works, or how to approach this problem, so any help or direction would be appreciated.
EDIT- EXAMPLE
otherarray = array(...prior array entries...,
array('Val1' => 'abc',
'Val2' => 'cde',
'Val3' => 'not important',
'Val4' => array(0 => 'subA', 1 => 'subB'),
...next array entries...);
BEFORE MERGE:
multiarray = array(...prior entries...,
array('Val1' => 'abc',
'Val2' => 'cde'));
valsarray = array(0 => 'subA', 1 => 'subB');
AFTER MERGE:
multiarray = array(...prior entries...,
array('Val1' => 'abc',
'Val2' => 'cde',
0 => 'subA',
1 => 'subB'));
So if multiarray was a regular array instead of a multidimensional one, I would do something like:
foreach($valsarray as $key => $val){
$multiarray[$key] = $val;
}
To achieve the end result.
I am not 100% sure what you are trying to accomplish a Minimal, Complete, and Verifiable example may help if I have misunderstood something.
It appears that the current() function does not work as you assume. (Or more specifically, the internal pointer.)
If you look at the example in the PHP documentation: Current(), you will see that for current($array) to change elements, you need to call next($array) or prev($array).
These function move the internal pointer of the array.
Note that in PHP 5, foreach loops use the internal pointer (and reset it when you start a loop), but in PHP 7, foreach loops do not use the internal pointer.
Anyway, here is my best guess at what could help you.
$valsarray_index = 0;
foreach ($otherarray as $other) {
$multiarray_value = array('Val1' => $other['Val1'], 'Val2' => $other['Val2']);
foreach ($other as $sub) {
$multiarray_value[$valsarray_index] = $sub;
// $multiarray_value["VALSARRAY_KEY" . $valsarray_index] = $sub;
$valsarray[] = $sub;
$valsarray_index += 1; // This stays in lockstep with the last index of $valsarray
}
$multiarray[] = $multiarray_value;
}
I am not exactly sure about what you want the final output to look like. If this produces incorrect information, then if would be helpful to provide some specific arrays for input and what you expect as output.
I have two arrays as shown below. I need to merge the content of the arrays so that I can get the structure as shown in the third array at last. I have checked array_merge but can't figure out the way this is possible. Any help appreciated. Thanks.
[
['gross_value' => '100', 'quantity' => '1'],
['gross_value' => '200', 'quantity' => '1']
]
and
[
['item_title_id' => '1', 'order_id' => '4'],
['item_title_id' => '2', 'order_id' => '4']
];
I should get a merged array like this:
[
[
'gross_value' => '100',
'quantity' => '1',
'item_title_id' => '1',
'order_id' => 4
],
[
'gross_value' => '200',
'quantity' => '1',
'item_title_id' => '2',
'order_id' => 4
]
]
Use array_merge_recursive :
Convert all numeric key to strings, (make is associative array)
$result = array_merge_recursive($ar1, $ar2);
print_r($result);
See live demo here
how about:
$arr1 = array(
0 => array(
'gross_value' => '100',
'quantity' => '1'
),
1 => array(
'gross_value' => '200',
'quantity' => '1'
)
);
$arr2 = array(
0 => array(
'item_title_id' => '1',
'order_id' => '4'
),
1 => array(
'item_title_id' => '2',
'order_id' => '4'
)
);
$arr = array();
foreach($arr1 as $k => $v) {
array_push($arr, array_merge($v, $arr2[$k]));
}
print_r($arr);
output:
Array
(
[0] => Array
(
[gross_value] => 100
[quantity] => 1
[item_title_id] => 1
[order_id] => 4
)
[1] => Array
(
[gross_value] => 200
[quantity] => 1
[item_title_id] => 2
[order_id] => 4
)
)
Have a look at array_merge
I would probably iterate over the arrays and merge them manually.
$result = array();
foreach ( $array1 as $key => $item )
{
$result[$key] = array_merge($array1[$key], $array2[$key]);
}
You will have an issue if the top-level arrays don't have strictly matching keys though.
If you have $array1 and $array2, try this:
foreach($array1 as $key1=>$innerArray){
$array1[$key1]['item_title_id'] = $array2[$key1]['item_title_id'];
$array1[$key1]['order_id'] = $array2[$key1]['order_id'];
}
The problem with things like merge recursive is that they don't know when to stop.
In some scenarios you want to stop traversing down an array and simply take a given value if it exists.
For instance if you have to override a nested config array you might not want the default keys to stick around at a a specific level.
here is my solution:
public static function merge_lvl2(){
$args = func_get_args();
return static::merge($args, 2);
}
public static function merge($args, $maxDepth = null, $depth = 1)
{
$merge = [];
foreach($args as $arg) {
if (is_array($arg)) {
if (is_array($merge)) {
if ($maxDepth == $depth) {
$arg += $merge;
$merge = $arg;
} else {
$merge = array_merge($merge, $arg);
}
} else {
$merge = $arg;
}
}
}
if ($maxDepth !== $depth) {
foreach($args as $a) {
if (is_array($a)) {
foreach($a as $k => $v) {
if (isset($merge[$k]) && is_array($merge[$k])) {
$merge[$k] = static::merge([$merge[$k], $v], $maxDepth, $depth + 1);
}
}
}
}
}
return $merge;
}
You can pass as many arrays to merge as you want to.
$merged = ClassName::merge_lvl2([..array1..], [..array2..], [..array3..], etc...);
It will stop merging at level 2 and accept the last instance of the key as an override instead of a merge.
You can also call merge directly with an array of args and setting the max depth.
If no max depth is set it will traverse the entire array.
The most modern, elegant, concise way to merge rows from two or more arrays (or the rows from a multidimensional array with 3 or more levels of depth) is to call array_merge() on each row (array_replace() can also be used). array_map() can call array_merge by its string name and the input data can be split into individual arguments with the "spread operator" (...) when needed.
Code for the OP's arrays: (Demo)
var_export(
array_map('array_merge', $arr1, $arr2)
);
The above technique will return a newly indexed array (though you might not notice because the sample input arrays were indexed to begin with). If your input data has associative first-level keys, they will be ignored and destroyed by this technique. If you have non-numeric first-level keys and want to merge on those, then array_merge_recursive() is likely to be the ideal native function - Demo.
However, it must be said, that for the OP's sample data array_merge_recursive() IS NOT a correct technique.
My first snippet is conveniently extended if you have more than two arrays which need their rows to be merge based on their positions. (Demo)
var_export(
array_map('array_merge', $arr1, $arr2, $arr3)
);
And as mentioned earlier, the spread operator can be used to unpack deeper arrays with the same result. Again, the number of subarrays containing rows can be dynamic. If your deep array only has one subarray containing rows, then the result will be a "flattening" effect where the top level is removed.
Code with new data structure: (Demo)
$masterArray = [
[
['gross_value' => '100', 'quantity' => '5'],
['gross_value' => '200', 'quantity' => '6']
],
[
['item_title_id' => '1', 'order_id' => '3'],
['item_title_id' => '2', 'order_id' => '4']
],
[
['foo' => 'bar1'],
['foo' => 'bar2']
]
];
var_export(
array_map('array_merge', ...$masterArray)
);
To be fair, array_replace_recursive() does provide the desired result using the OP's sample data, but I find the technique to be semantically misleading. (Demo)
All that said, you are not forced to use PHP's native array functions; you can use classic loops as well -- you will have several ways to "unite" the rows inside the loop. This approach is a little less D.R.Y. because you need to explicitly specific the separate arrays that you wish to synchronously iterate. Just make sure that you understand the nuanced differences in using array_merge(), array_replace(), and the union operator (+) with different qualities of data. Hint: associative, non-numeric keyed data in the respective rows will be affected when key collisions occur. Be careful to use a merging technique that will not overwrite the wrong data when rows share associative keys.
array_merge() to build a new array: (array_merge() Demo) (array_replace() Demo) (array union operator + Demo)
$result = [];
foreach ($arr1 as $i => $row) {
$result[] = array_merge($row, $arr2[$i]);
}
var_export($result);
Alternative, you can use the same general approach, but instead of populating a new $result array, you can merge data into the first array.
(array_merge() Demo) (array_replace() Demo) (array union assignment operator += Demo)
foreach ($arr1 as $i => &$row) {
$row = array_merge($row, $arr2[$i]);
}
var_export($arr1);
For the sake of completeness, if you have an indexed array of indexed arrays, then you might even use iterated calls of array_push() with the spread operator to achieve a similar functionality. This quite literally, indicatively appends the row data from subsequent arrays to the first array's rows.
Code: (Demo)
$arr1 = [
['A', 'B', 'C'],
['F', 'G']
];
$arr2 = [
['D', 'E'],
['H', 'I', 'J', 'L']
];
foreach ($arr1 as $i => &$row) {
array_push($row, ...$arr2[$i]);
}
var_export($arr1);
Related non-duplicate content on Stack Overflow:
Partially merge one array's row data with another another array:Add column of values from one array to another
Merge two flat arrays to create an array of merged rows:Transforming array values in elements of a subarray using PHP
Merge rows with indexed elements, remove duplicates and reindex:Merge two multidimensional arrays, preserve numeric keys, and combine values inside array
Merge arrays containing objects:Merge rows of two arrays containing objects by first level index
Push single elements from one array to rows in another array:Push elements from one array into rows of another array (one element per row)
On the above pages, the rabbit hole goes further because I've linked other related pages to them. Keep researching until you find what you need.
If you are using Laravel, you might be interested in its combine() and collect() methods.