This is an interesting situation which I created a working function for, but wondering if I just anyone had any simpler methods for this.
I have the following multidimensional array:
$foo = array(
[0] => array(
'keys' => array(
'key1' => 1,
'key2' => a,
'key3' => 123
),
'values' => array(
//goodies in here
)
)
[1] => array(
'keys' => array(
'key1' => 1,
'key2' => b,
'key3' => 456
),
'values' => array(
//goodies in here
)
)
)
What I wanted, was to transform this into a multidimensional array nested based on the values from the keys array, the output I was looking for is:
$bar = array(
[1] => array(
[a] => array(
[123] => array( //values array from above )
),
[b] => array(
[456] => array( //values array from above )
)
)
)
The keys can always be nested based on their position in the keys array, but the keys themselve are not always the same, keys handles a user defined grouping, so the order and values can change. I also didn't want duplicate keys.
array_merge failed me because in a lot of cases, the array keys are actually numeric ids. So, this function works - but I'm wondering if I made myself a new pair of gloves.
protected function convertAssociativeToMulti( &$output, $keys, $value )
{
$temp = array();
$v = array_values( $keys );
$s = sizeof( $v );
for( $x = 0; $x < $s; $x++ )
{
$k = $v[ $x ];
if ( $x == 0 )
{
if ( !array_key_exists( $k, $output ) )
$output[ $k ] = array();
$temp =& $output[ $k ];
}
if ( $x && ( $x + 1 ) !== $s )
{
if( !array_key_exists( $k, $temp ) )
$temp[ $k ] = array();
$temp =& $temp[$k];
}
if ( ( $x + 1 ) == $s )
$temp[$k] = $value;
}
}
$baz = array();
foreach( $foo as $bar )
{
$this->convertAssociativeToMulti( $baz, $bar['keys'], $bar['values'] );
}
So, how do you do this more simply / refactor what I have?
This is a bit more concise (See it in action here):
$bar=array();
foreach($foo as $item)
{
$out=&$bar;
foreach($item['keys'] as $key)
{
if(!isset($out[$key])) $out[$key]=array();
$out=&$out[$key];
}
foreach($item['values'] as $k=>$v) $out[$k]=$v;
// $out=array_merge($out,$item['values']); -- edit - fixed array_merge
}
var_dump($bar);
Related
I'm not able to find a solution for the given scenario where I have a multidimensional array in which I have to find the sum of the values of the arrays that have same ID.
demo.php
$array = array("1"=>array("id"=>"1", "total"=>"100"),
"2"=>array("id"=>"2", "total"=>"300"),
"3"=>array("id"=>"3", "total"=>"400"),
"4"=>array("id"=>"4", "total"=>"500"),
"5"=>array("id"=>"1", "total"=>"560"));
I want to get the sum of the total of all the duplicates IDs e.g 1 is the ID and the sum of the total would be 100 + 560 = 650.
One option is using array_reduce to summarize the array into an associative array.
$array = //....
$result = array_reduce( $array, function( $c, $v ){
if ( !isset( $c[ $v["id"] ] ) ) $c[ $v["id"] ] = 0;
$c[ $v["id"] ] += $v["total"];
return $c;
}, array() );
$result will be:
Array
(
[1] => 660
[2] => 300
[3] => 400
[4] => 500
)
UPDATE
Add another reduce to group the array first. The second reduce is to only include the array elements with more than one count.
$array = //....
$group = array_reduce( $array, function( $c, $v ){
if ( !isset( $c[ $v["id"] ] ) ) $c[ $v["id"] ] = [ "count" => 0, "total" => 0, "id" => $v["id"] ];
$c[ $v["id"] ]["count"] += 1;
$c[ $v["id"] ]["total"] += $v["total"];
return $c;
}, array() );
$result = array_reduce( $group, function( $c, $v ){
if ( $v["count"] > 1 ) $c[ $v["id"] ] = $v["total"];
return $c;
}, array() );
This will result to:
Array
(
[1] => 660
)
Another take while using a foreach:
$array = array("1"=>array("id"=>"1", "total"=>"100"),
"2"=>array("id"=>"2", "total"=>"300"),
"3"=>array("id"=>"3", "total"=>"400"),
"4"=>array("id"=>"4", "total"=>"500"),
"5"=>array("id"=>"1", "total"=>"560"));
$total = [];
foreach ($array as $sub) {
$total[$sub['id']] = isset($total[$sub['id']]) ? $total[$sub['id']] += $sub['total'] : $total[$sub['id']] = $sub['total'];
}
Output
Array
(
[1] => 660
[2] => 300
[3] => 400
[4] => 500
)
Live Example
Repl
Reading Material
Ternary Operator
You can use simple foreach loop to get the expected result
<?php
// Your code here!
$array = array("1"=>array("id"=>"1", "total"=>"100"),
"2"=>array("id"=>"2", "total"=>"300"),
"3"=>array("id"=>"3", "total"=>"400"),
"4"=>array("id"=>"4", "total"=>"500"),
"5"=>array("id"=>"1", "total"=>"560"));
$issetArray = array();
foreach ($array as $key => $value) {
if(isset($issetArray[$value['id']]))
{
$issetArray[$value['id']]['total'] += $value['total'];
}
else
{
$issetArray[$value['id']] = array();
$issetArray[$value['id']] = $value;
}
}
$result = array();
foreach ($issetArray as $value) {
array_push($result, $value);
}
print_r($result);
?>
Example: https://paiza.io/projects/Fnay-hbk4Cuf_-rMTL5AFA
If you want to use the external class tableArray and you want to preserve the structure of the input array try this:
$array = array("1"=>array("id"=>"1", "total"=>"100"),
"2"=>array("id"=>"2", "total"=>"300"),
"3"=>array("id"=>"3", "total"=>"400"),
"4"=>array("id"=>"4", "total"=>"500"),
"5"=>array("id"=>"1", "total"=>"560"));
$newData = tableArray::create($array)
->filterGroupSum('total',['id'])
->fetchAll()
;
$newData:
array (
0 =>
array (
'id' => "1",
'total' => 660,
),
1 =>
array (
'id' => "2",
'total' => 300,
),
2 =>
array (
'id' => "3",
'total' => 400,
),
3 =>
array (
'id' => "4",
'total' => 500,
),
)
So, I am getting unique values from my MD array utilizing the following function:
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;
}
My array is similar to the following:
Array (
[0] =>
Array (
'name' => 'Nevada'
)
[1] =>
Array (
'name' => 'Colorado'
)
[2] =>
Array (
'name' => 'Nevada'
)
[3] =>
Array (
'name' => 'Colorado'
)
[4] =>
Array (
'name' => 'Oklahoma'
)
[5] =>
Array (
'name' => 'Nevada'
)
[6] =>
Array (
'name' => 'Nevada'
)
)
And using the function (unique_multidim_array ( $term_arr, 'name' )) above I am getting a single Nevada and a single Colorado, however, it is not returning Oklahoma
What can I do to ensure that it will return unique values, even if there are no duplicates?
Your resulting array keeps the original indices, and, depending on how you are iterating over it, you might get unexpected results. Try resetting the indices:
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[] = $val; // <--- remove the $i
}
$i++;
}
return $temp_array;
}
Or, as you say, array_values() will help too:
$term_arr = array_values ( unique_multidim_array ( $term_arr, 'name' ) );
PHP already has a function to remove duplicates from an array
array_unique(array)
should do the trick
I have pairs of keys identified by their respective ID like this:
array(
'key_a_0' => $a,
'key_a_1' => $a,
'key_b_0' => $b,
'key_b_1' => $b
)
I need this structure:
array(
'0' => array(
'key_a' => $a,
'key_b' => $b
),
'1' => array(
'key_a' => $a,
'key_b' => $b
)
)
What would be the best way to achieve this?
Provided this is exactly how all the data is present as, and stays as, this would then be simple to amend into the format you require with a simple foreach loop.
$new = array();
foreach($data as $key => $variable){
list($name,$var,$index) = explode("_", $key);
$new[$index][$name . '_' . $var] = $variable;
}
This returns;
Array
(
[0] => Array
(
[key_a] => 5
[key_b] => 10
)
[1] => Array
(
[key_a] => 5
[key_b] => 10
)
)
Example
Ideally - you'd want to set your array structure at creation, as Dagon said.
The following should do the job. This assumes, however, that the initial array comes in a specific order and that order is what is expected within each nested array in the final array, and that the keys in the initial array are always in that format.
$a = 5;
$b = 10;
$aRawArray = array(
'key_a_0' => $a,
'key_a_1' => $a,
'key_b_0' => $b,
'key_b_1' => $b,
);
$aFormattedArray = array();
foreach ($aRawArray as $sKey => $mValue) {
$aKeyParts = explode('_', $sKey);
$sExtractedKey = $aKeyParts[2];
$sNewKey = $aKeyParts[0] . '_' . $aKeyParts[1];
if (!isset($aFormattedArray[$sExtractedKey])) {
$aFormattedArray[$sExtractedKey] = array();
}
$aFormattedArray[$sExtractedKey][$sNewKey] = $mValue;
}
var_dump($aFormattedArray);
I have a basic function that takes two arrays and merges them based on key. It is meant to work with arrays that have different keys but ultimately the same number of items. The function works well. However, it only inputs two arrays at a time to merge.
How can I modify so it takes n number of arrays to merge?
PHP
protected function arrayMergeKeys( $a, $b )
{
$retArr = array();
// I used min(countA, countB) so there will be no errors on
// unbalanced array item counts.
for ( $i = 0; $i < min( count( $a ), count( $b ) ); $i++ ) {
$retArr[ $i ] = array_merge( $a[ $i ], $b[ $i ] );
} // for end
return $retArr;
}
Desired result is to call the function with three arrays: $merged_arrays = arrayMergeKeys($a, $b, $c);
Input
//$a
Array
(
[a] => 'new value'
[b] => 2
[c] => 3
)
//$b
Array
(
[1] => 'blah'
[2] => 'dud'
[3] => 697
)
//$c
Array
(
[pet] => 'dog'
[breed] => 'german shepher'
[age] => 100
)
Output
Array
{
[a] => 'new value'
[1] => 'blah'
[pet] => 'dog'
}
Array
{
[b] => 2
[2] => 'dud'
[breed] => 'german shepher'
}
Array
{
[c] => 3
[3] => 697
[age] => 100
}
Leaving your original function untouched you could do it like this:
function arrayMulitMergeKeys() {
$arrayArgs = func_get_args();
$c = count($arrayArgs);
$result = arrayArgs[0];
for($i = 1; $i < $c; $i++) {
$result = arrayMergeKeys($result, $arrayArgs[$i]);
}
return $result;
}
This can be optimized further please re-check it once again
function arrayMergeKeys()
{
$retArr = array();
$arrayArgs = func_get_args();
//print_r($arrayArgs);
$minCount = count($arrayArgs[0]);
foreach($arrayArgs as $array){
if($minCount > count($array)){
$minCount = count($array);
}
}
// I used min(countA, countB) so there will be no errors on
// unbalanced array item counts.
for ( $i = 0; $i < $minCount; $i++ ) {
foreach($arrayArgs as $array){
$iCount = count($array);
for($j=0; $j<=$iCount; $j++)
{
if($i==$j){
$arrayKeys = array_keys($array);
if(isset($arrayKeys[$i])){
$retArr[ $i ][$arrayKeys[$i]] = $array[$arrayKeys[$i]];
break;
}
}
}
}
} // for end
return $retArr;
}
$a = Array
(
'a' => 'new value',
'b' => 2,
'c' => 3
);
$b = array
(
'1' => 'blah',
'2' => 'dud',
'3' => 697
);
$c = array
(
'pet' => 'dog',
'breed' => 'german shepher',
'age' => 100
);
print_r(arrayMergeKeys($a, $b, $c));
Here's some pseudo code to point you in the right direction.
function mergeArrays(a, b, c)
arrays = []
shortestLength = min(a.len, b.len, c.len)
for i in [0 .. shortestLength - 1]
arrays[i] = [ a[i], b[i], c[i] ];
return arrays
If you're doing this as an exercise, I would suggest implementing the following also:
mergeArrays(arr) where arr is an array of arrays that need to be merged.
Haven't tested, but this should work
protected function arrayMergeKeys(...$arrs) {
$retArr = array();
// I used min(countA, countB) so there will be no errors on
// unbalanced array item counts.
$countArr = array();
foreach ($arrs as $arr) {
$countArr[] = count($arr);
}
$min = min($countArr);
for ( $i = 0; $i < $min; $i++ ) {
$mergeArr = array();
foreach ($arrs as $arr) {
$mergeArr[] = $arr[$i];
}
$retArr[ $i ] = array_merge( $mergeArr );
} // for end
return $retArr;
}
You can simply 'add' the arrays:
$a = array
(
'a' => 'new value',
'b' => '2',
'c' => '3'
);
//$b
$b = array
(
'1' => 'blah',
'2' => 'dud',
'3' => '697'
);
//$c
$c = array
(
'pet' => 'dog',
'breed' => 'german shepher',
'age' => 100
);
$result= $a+$b+$c;
print_r($result);
output
Array ( [a] => new value [b] => 2 [c] => 3 [1] => blah [2] => dud [3] => 697 [pet] => dog [breed] => german shepher [age] => 100 )
I know there's a ton of answers but I can't seem to get it right. I have the following arrays and what I've tried:
$a = array ( 0 => '1421' , 1 => '2241' );
$b = array ( 0 => 'teststring1' , 1 => 'teststring2' );
$c = array ( 0 => 'teststring3' , 1 => 'teststring4' );
$d = array ( 0 => 'teststring5' , 1 => 'teststring6' );
$e = array_combine($a, array($b,$c,$d) );
But with this I get the error array_combine() [function.array-combine]: Both parameters should have an equal number of elements.
I know it's because the $a's array values aren't keys. That's why I'm coming here to see if I could get some help with an answer that can help me make it look something like this:
array(2) {
[1421]=>array( [0] => teststring1
[1] => teststring3
[2] => teststring5
)
[2241]=>array( [0] => teststring2
[1] => teststring4
[2] => teststring6
)
}
If you have control over creating the arrays, you should create them like:
$a = array ('1421' ,'2241');
$b = array ('teststring1', 'teststring3', 'teststring5');
$c = array ('teststring2', 'teststring4', 'teststring6');
$e = array_combine($a, array($b,$c) );
If not, you have to loop over them:
$result = array();
$values = array($b, $c, $d);
foreach($a as $index => $key) {
$t = array();
foreach($values as $value) {
$t[] = $value[$index];
}
$result[$key] = $t;
}
DEMO
Here is a one-liner in a functional coding style. Calling array_map() with a null function parameter followed by the "values" arrays will generate the desired subarray structures. array_combine() does the key=>value associations.
Code (Demo)
var_export(array_combine($a, array_map(null, $b, $c, $d)));
Output:
array (
1421 =>
array (
0 => 'teststring1',
1 => 'teststring3',
2 => 'teststring5',
),
2241 =>
array (
0 => 'teststring2',
1 => 'teststring4',
2 => 'teststring6',
),
)
Super clean, right? I know. It's a useful little trick when you don't have control of the initial array generation step.
Here's a new version of array_merge_recursive which will handle integer keys. Let know how it goes.
$a = array ( 0 => '1421' , 1 => '2241' );
$b = array ( 0 => 'teststring1' , 1 => 'teststring2' );
$c = array ( 0 => 'teststring3' , 1 => 'teststring4' );
$d = array ( 0 => 'teststring5' , 1 => 'teststring6' );
$e = array_combine($a, array_merge_recursive2($b, $c, $d));
echo "<pre>";
print_r($e);
function array_merge_recursive2() {
$args = func_get_args();
$ret = array();
foreach ($args as $arr) {
if(is_array($arr)) {
foreach ($arr as $key => $val) {
$ret[$key][] = $val;
}
}
}
return $ret;
}