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.
Related
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.
Using the numbers from $ids, I want to pull the data from $nuts.
So for example:
$ids = [0,3,5]; // 0 calories, 3 sugar, 5 fat
$nuts = [
'calories' => 'cal',
'protein' => 'pro',
'carbohydrate' => 'car',
'sugar' => 'sug',
'fiber' => 'fib',
'fat' => 'fat',
];
$returnData = [
'calories' => 'cal',
'sugar' => 'sug',
'fat' => 'fat',
];
I could loop through each $ids number with a foreach(); but I'm curious to see if there is a better method than this?
$newNuts = array_values(array_flip($nuts));
foreach($ids as $i)
$returnData[$newNuts[$i]] = $nuts[$newNuts[$i]];
I did some work and realized, you don't need array_flip, array_values is fine.
$num_nuts = array_values ($nuts);
for ($z=0; $z<sizeof($ids); $z++) {
echo $num_nuts[$ids[$z]];
}
Just 1 more line of code, but I think it does the job. I think mine is going to be faster because the array_flip basically exchanges all keys with their associated values in an array, which is not what I am doing. It's actually one less pain.
I am simply converting the original array to a new one by index and simply looping upon it. Also, not the elegant way to use the power of PHP available to us, but works just fine. array_flip is O(n), but I think better not use it for larger data-sets.
How about a simple array_slice?
$result = array();
foreach ($ids as $i) {
$result += array_slice($nuts, $i, 1, true);
}
No need to create a copy of the array.
if i have the following array:
array(
'var1' => 123,
'var2' => 234,
'var3' => 345
);
I would like to extract specific parts of this to build a new array i.e. var1 and var3.
The result i would be looking for is:
array(
'var1' => 123,
'var3' => 345
);
The example posted is very stripped down, in reality the array has a much larger number of keys and I am looking to extract a larger number of key and also some keys may or may not be present.
Is there a built in php function to do this?
Edit:
The keys to be extracted will be hardcoded as an array in the class i..e $this->keysToExtract
$result = array_intersect_key($yourarray,array_flip(array('var1','var3')));
So, with your edit:
$result = array_intersect_key($yourarray,array_flip($this->keysToExtract));
You don't need a built in function to do this, try this :
$this->keysToExtract = array('var1', 'var3'); // The keys you wish to transfer to the new array
// For each record in your initial array
foreach ($firstArray as $key => $value)
{
// If the key (ex : 'var1') is part of the desired keys
if (in_array($key, $this->keysToExtract)
{
$finalArray[$key] = $value; // Add to the new array
}
}
var_dump($finalArray);
Note that this is most likely the most efficient way to do this.
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.
I have this multidimensional array which I'll name "original":
$original=
array
0 =>
array
'animal' => 'cats'
'quantity' => 1
1 =>
array
'animal' => 'dogs'
'quantity' => '1'
2 =>
array
'animal' => 'cats'
'quantity' => '3'
However, I want to merge internal arrays with the same animal to produce this new array (with quantities combined):
$new=
array
0 =>
array
'animal' => 'cats'
'quantity' => 4
1 =>
array
'animal' => 'dogs'
'quantity' => '1'
I understand that there are similar questions on stackoverflow, but not similar enough for me to be able to figure out how to use the feedback those questions have gotted to apply to this specific example. Yes, I know I probably look stupid to a lot of you, but please remember that there was a time when you too didn't know crap about working with arrays :)
I've tried the following code, but get Fatal error: Unsupported operand types (Referring to line 11). And if I got that error to go away, I'm not sure if this code would even produce what I'm trying to achieve.
$new = array();
foreach($original as $entity){
if(!isset($new[$entity["animal"]])){
$new[$entity["animal"]] = array(
"animal" => $entity["animal"],
"quantity" => 0,
);
}
$new[$entity["animal"]] += $entity["quantity"];
}
So, I don't know what I'm doing and I could really use some help from the experts.
To try to give a super clear question, here goes... What changes do I need to make to the code so that it will take $original and turn it into $new? If the code I provided is totally wrong, could you provide an alternative example that would do the trick? Also, the only language I am familiar with is PHP, so please provide an example using only PHP.
Thank you
You're very close.
$new[$entity["animal"]] += $entity["quantity"];
needs to be
$new[$entity["animal"]]['quantity'] += $entity["quantity"];
In your if ( !isset [...] ) line, you're setting $new[$entity['animal']] to an array, so you need to access the 'quantity' field of that array before trying to add the new quantity value to it.
One of the reasons why your code is not working is that you're using the animal name as the array index, not the integer index which is used in your desired output.
Try this:
$new = array(); // Desired output
$map = array(); // Map animal names to index in $new
$idx = 0; // What is the next index we can use
foreach ($original as $entity) {
$animal = $entity['animal'];
// If we haven't saved the animal yet, put it in the $map and $new array
if(!isset($map[$animal])) {
$map[$animal] = $idx++;
$new[$map[$animal]] = $entity;
}
else {
$new[$map[$animal]]['quantity'] += $entity['quantity'];
}
}
This works:
$new = array();
$seen = array();
foreach($original as $entity) {
// If this is the first time we're encountering the animal
if (!in_array($entity['animal'], $seen)) {
$new[] = $entity;
$seen[] = $entity['animal'];
// Otherwise, if this animal is already in the new array...
} else {
// Find the index of the animal in the new array...
foreach($new as $index => $new_entity) {
if ($new_entity['animal'] == $entity['animal']) {
// Add to the quantity
$new[$index]['quantity'] += $entity['quantity'];
}
}
}
}
Your example was using the animal name as the index, yet the actual index is just an integer.
However, I think the resulting array would be easier to use and easier to read if it was formatting like this instead:
array('cats' => 4, 'dogs' => 1)
That would require different but simpler code than above... but, it wouldn't be a direct response to your question.