PHP - Unexpected array_merge_recursive() output - php

I have this code
$a1 = array(
'success' => TRUE,
'data' => array(
'foo' =>
array(
21 =>
array(
1 =>
array(1, 2, 3, 4, 5)
)
)
)
);
$a2 = array(
'success' => TRUE,
'data' => array(
'foo' =>
array(
21 =>
array(
7 =>
array(6, 7, 8, 9, 10)
)
)
)
);
$results = array();
$results = array_merge_recursive($results, $a1['data']);
$results = array_merge_recursive($results, $a2['data']);
var_dump($results);
From what I understood of array_merge_recursive(), I am expecting the results would be
array
'foo' =>
array
21 =>
array
1 =>
array
0 => int 1
1 => int 2
2 => int 3
3 => int 4
4 => int 5
7 =>
0 => int 6
1 => int 7
2 => int 8
3 => int 9
4 => int 10
Instead I get this
array
'foo' =>
array
21 =>
array
1 =>
array
0 => int 1
1 => int 2
2 => int 3
3 => int 4
4 => int 5
22 =>
array
7 =>
array
0 => int 6
1 => int 7
2 => int 8
3 => int 9
4 => int 10
Where did the 22 index come from? Why is it outputting differently? Did I use the function wrong?

array_merge_recursive merges elements/arrays from the same depth as the first array, but if both arrays the key is a numerical index and they are the same it then appends to it. This is what is happening in your situation. since then your array is appended at 2nd level where index 21 is found by creating index 22. To receive the desired output you have change your index 21 to a string key like "x21"
Notes from php manual
If the input arrays have the same string keys, then the values for
these keys are merged together into an array, and this is done
recursively, so that if one of the values is an array itself, the
function will merge it with a corresponding entry in another array
too. If, however, the arrays have the same numeric key, the later
value will not overwrite the original value, but will be appended.

I just came across the same issue, I wanted to merge the arrays but surprisingly found the keys were changed automatically in the result. The reason was because my "keys" are string of decimal numbers, without any alphabetic characters.
But luckily I noticed that if the keys have alphabetic characters, they could be reserved. So just came up with the following idea, which would append a letter 'S' to each key recursively before the merge, and later remove it in the final result.
Please refer to the enhanced_array_merge_recursive function for details:
<?php
$aa = [
'10' => 'book',
'14' => ['cat'],
];
$ab = [
'12' => 'cd',
'18' => 'cup',
'14' => ['dog'],
];
var_dump(enhanced_array_merge_recursive($aa, $ab));
function revise_keys($source)
{
if (!is_array($source)) {
return $source;
}
$target = [];
foreach ($source as $key => $value) {
$target['S' . $key] = revise_keys($value);
}
return $target;
}
function revert_keys($source)
{
if (!is_array($source)) {
return $source;
}
$target = [];
foreach ($source as $key => $value) {
$target[substr($key, 1 - strlen($key))] = revert_keys($value);
}
return $target;
}
function enhanced_array_merge_recursive(...$candidates)
{
$merged = [];
foreach ($candidates as $candidate) {
if (!is_array($candidate)) {
continue;
}
$merged = array_merge_recursive($merged, revise_keys($candidate));
}
return revert_keys($merged);
}
Output looks like following:
array(4) {
[10] =>
string(4) "book"
[14] =>
array(1) {
[0] =>
array(2) {
[0] =>
string(3) "cat"
[1] =>
string(3) "dog"
}
}
[12] =>
string(2) "cd"
[18] =>
string(3) "cup"
}

Related

sorting chapters in an 4 level array

I want to sort an existing array into 4 dimension array:
the "list" array that I have:
1 => "text1"
10 => "text10"
11 => "text11"
101 => "text101"
1011 => "text1011"
10123 => "text10123"
2 => "text2"
20 => "text20"
201 => "text201"
2011 => "text2011"
20111 => "text20111"
the array I want is one that has all the data sorted by each number (in 4 dimention) i mean that i won't have an other array in the end of $chapter[1] containing 10123 => "text10123" (this one will be in the same array as this one : 1011 => "text1011"
here is an exemple of the array i want
$chapter[1] = array(
1 => "text1", array(
10 => "text10", 11 => "text11", array(
101 => "text101", array(
1011 => "text1011", 10123 => "text10123" )
)
)
);
I guess you can use for-loop and break each number to digits (with str-split) and then add the arrays.
Consider the following example:
$arr = array(1, 11, 111, 113, 2, 21, 211, 213);
$chapter = array(); // this will be your result array
foreach($arr as $e) {
$digits = str_split($e);
$current = &$chapter;
foreach($digits as $d) {
if (!isset($current[$d]))
$current[$d] = array();
$current = &$current[$d];
}
}
Notice I used & to assign the new array to the original result one.
I know your array has missing key and not have to be sorted but I guess you can overcome it (filter and sort the array before)
Edited
After question has change this is the example code: (when key DATA is for the text you want and key CHILDREN for the next elements)
$arr = array(1 => "text1", 10 => "text10", 11 => "text11", 101 => "text101", 1011 => "text1011", 10123 => "text10123", 2 => "text2", 20 => "text20", 201 => "text201", 2011 => "text2011", 20111 => "text20111");
$chapter = array();
foreach($arr as $key => $val) {
$digits = str_split(substr($key, 0, 4)); // if 4 digits is the max depth
$current = &$chapter;
foreach($digits as $d) {
if (!isset($current["CHILDREN"][$d]))
$current["CHILDREN"][$d] = array();
$current = &$current["CHILDREN"][$d];
}
$current["DATA"] = $val;
}

Array search multiple values in consecutive order

Take the given arrays:
// Original
array:14 [
0 => "hello"
1 => "i"
2 => "like"
3 => "cats"
4 => "they're"
5 => "cute"
6 => "and"
7 => "cuddly"
8 => "you"
9 => "know"
10 => "well"
11 => "i"
12 => "love"
13 => "cats"
]
// Sliced
array:6 [
0 => "like"
1 => "cats"
2 => "they're"
3 => "cute"
4 => "and"
5 => "cuddly"
]
I want to check the original array for my sliced values and retrieve their original keys. This must occur only when consecutive values match.
Ultimately, something like:
return array_keys_from_consec_values($original, $sliced);
// Result
array:14 [
0 => 2
1 => 3
2 => 4
3 => 5
4 => 6
5 => 7
]
Notes
sliced is not a result of using array_slice() on the original array. Otherwise I would have used that function and utilised the preserve keys parameter.
Want to avoid ambiguities. For example, a simple array_search for cats would return the key 3, but 13 also exists. Which might throw the function off.
In the event of any of the sliced values not existing in original (in order) an empty array should be returned. An example of a sliced array that would fail:
// Sliced
array:6 [
0 => "like"
1 => "cats"
2 => "they're"
3 => "cut3"
4 => "and"
5 => "cuddly"
]
Any recommendations, got speed in mind...
From what I understood, you want to get the index from the $original array based on values in $slice, you can use the array_flip() and the array_unique() functions:
function array_keys_from_consec_values($original, $slice){
// Get rid of duplicates and flip the keys with the values
$index = array_unique($original);
$index = array_flip($index);
$result = [];
// Loop through the slice
foreach($slice as $key => $value){
// Check if the value exists in the indexed array
if(!isset($index[$value])){
// Return an empty array
return array();
}
// And get the key from the flipped array
$result[$key] = $index[$value];
}
return $result;
}
var_dump(array_keys_from_consec_values($original, $slice));
This will give:
array (size=6)
0 => int 2
1 => int 3
2 => int 4
3 => int 5
4 => int 6
5 => int 7
This works on your example data and may be good enough:
$r = array_keys(array_unique(array_intersect($o, $s)));
If not, since you have a slice:
if(($k = array_search($s[0], $o)) === false ||
array_values($r = array_slice($o, $k, count($s), true)) != $s) {
$r = [];
}
Find the first key of the original from the first value of the slice
Slice from the original (preserving keys) and compare to the slice
For your example this yields:
Array
(
[2] => like
[3] => cats
[4] => they're
[5] => cute
[6] => and
[7] => cuddly
)
I see after the fact that you want to get the keys:
$r = array_values(array_flip($r));
If the slice is an actual array_slice from the original then pass true as the forth parameter to preserve the keys.
Good effort people, I admit the request was slightly hard to explain - so didn't get exactly what I was looking for. I realised that what I wanted was essentially an array_search that returns multiple keys when the needles are found consecutively. I've gone ahead and made the function myself:
/**
* Search for consecutive needles in haystack and return the corresponding keys.
*
* #param array $needles => Array values.
* #param array $haystack => Array to search.
* #param int $searchDirection => [0] (forwards) | [1] (backwards).
* #return array
*/
function array_search_all_consec(array $needles, array $haystack, $searchDirection = 0)
{
$needlesInScope = array_values($needles); // Keys not relevant, reset them.
$haystackInScope = $haystack;
if (in_array(reset($needlesInScope), $keys = array_keys($haystackInScope))) {
$needlesLength = count($needlesInScope);
foreach (($searchDirection == 0 ? $keys : array_reverse($keys)) as $offset) {
$length = $offset + $needlesLength;
$sliced = array_slice($haystackInScope, $offset, $length);
if (empty(array_diff_assoc($needlesInScope, $sliced))) {
return range($offset, $length - 1);
}
}
}
return [];
}
Test 1
$o = [
0 => "hello",
1 => "i",
2 => "like",
3 => "cats",
4 => "they",
5 => "cute",
6 => "and",
7 => "cuddly",
8 => "you",
9 => "know",
10 => "well",
11 => "i",
12 => "love",
13 => "cats",
];
$s = [
"i",
"love",
"cats",
];
return array_search_all_consec($s, $o);
// Result
array(3) {
[0] =>
int(11)
[1] =>
int(12)
[2] =>
int(13)
}
Test 2
$s = [
"i",
"like",
"cats",
];
return array_search_all_consec($s, $o);
// Result
array(3) {
[0] =>
int(1)
[1] =>
int(2)
[2] =>
int(3)
}
Test 3
$s = [
"i",
"lov3",
"cats",
];
return array_search_all_consec($s, $o);
// Result
array(0) {
}
Test 4
$s = [
"cats",
"i",
"love",
];
return array_search_all_consec($s, $o);
// Result
array(0) {
}
Test 5
$s = [
"i",
"love",
"cats",
"blah",
];
return array_search_all_consec($s, $o);
// Result
array(0) {
}
So there you have it. If anyone can improve the efficiency of the function please do contribute!

Count the keys of multi-dimension array php

Maybe the title can not explain my question ,please see my example :
I have an multi-dimension array like this :
Array
(
[0] => Array
(
[name] => 'A'
[ec_dest_name] => 楽天testuser_998
),
[1] => Array
(
[name] => 'A'
[ec_dest_name] => 楽天testuser_998
),
[2] => Array
(
[name] => 'B'
[ec_dest_name] => 楽天testuser_998
),
[3] => Array
(
[name] => 'C'
[ec_dest_name] => 楽天testuser_998
)
)
I want to count the element by key name , it mean that I want to return an array like :
Array ('A' => 2 , 'B'=>1, 'C'=>1)
Any quick way to accomplish that , I could loop array and count but I think it is not a good idea
Thank in advanced
You can use array_count_values & array_column togather -
$counts = array_count_values(array_column($your_array, 'name'));
Output
array(3) {
["A"]=>
int(2)
["B"]=>
int(1)
["C"]=>
int(1)
}
Demo
As Mark Baker suggested for older PHP versions -
$counts = array_count_values(
array_map(function($value) {
return $value['name'];
}, $your_array)
);
You may as well do that with 2 Loops as shown below. You might test this also HERE.
<?php
$arrSections = array();
$arrCounts = array();
$arrMain = array(
array(
'name' => "A",
'ec_dest_name' => "楽天testuser_998",
),
array(
'name' => "A",
'ec_dest_name' => "楽天testuser_998",
),
array(
'name' => "B",
'ec_dest_name' => "楽天testuser_998",
),
array(
'name' => "C",
'ec_dest_name' => "楽天testuser_998",
),
);
// BUNDLE ARRAYS WITH SIMILAR name INTO ONE GROUP
// THUS CREATING A MULTI-DIMENSIONAL ARRAY WHOSE MAIN KEYS CORRESPOND TO
// THE name OF THE MEMBER ARRAYS IN THE GROUP.
foreach($arrMain as $iKey=>$subMain){
$name = $subMain['name'];
if(!array_key_exists($name, $arrSections)) {
$arrSections[$name] = array();
}
$arrSections[$name][] = $subMain;
}
// FETCH THE COUNTS OF EACH GROUP AND MAKE AN ARRAY OUT OF IT...
foreach($arrSections as $k=>$v){
$arrCounts[$k] = count($v);
}
var_dump($arrCounts);
//OUTPUTS::
array (size=3)
'A' => int 2
'B' => int 1
'C' => int 1

Combine array in php [duplicate]

This question already has answers here:
Add 2 values to 1 key in a PHP array
(7 answers)
Closed last month.
I have array
array
0 =>
array
2 => int 50000
1 =>
array
2 => int 30000
2 =>
array
1 => int 25000
3 =>
array
1 => int 20000
4 =>
array
1 => int 10000
I need to create array the result is:
array
2 => int 50000
2 => int 30000
1 => int 25000
1 => int 20000
1 => int 10000
Thank for all.
sorry, my english very bad :(
PHP doesn't allow arrays to have the same keys. This will show how php will handle a foreach loop wich is rewriting the array into a new one with desired key and value
$array = array(
0 =>
array(
2 => 50000),
1 =>
array(
2 => 30000),
2 =>
array(
1 => 25000),
3 =>
array(
1 => 20000),
4 =>
array(
1 => 10000)
);
$new_array = array();
foreach($array as $data)
{
foreach($data as $key => $val)
{
$new_array[$key] = $val;
}
}
var_dump($new_array);
This will output
array(2) {
[2]=>
int(30000)
[1]=>
int(10000)
}
Live Sample
As you can see keys are overwritten on each loop becuase they are identical and so are values, i think you can use the above function to have a one level array removing keys from $new_array
foreach($data as $key => $val)
{
$new_array[] = $val;
}
Live Sample
This does what you want (without preserving children keys, since you cannot have multiple elements with the same key):
$flat_array = array_map('current', $array);
Try here: http://codepad.org/1h7mKbqe

How do I use array_unique on an array of arrays?

I have an array
Array(
[0] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[1] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[2] => Array
(
[0] => 33
[user_id] => 33
[1] => 8
[frame_id] => 8
)
[3] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[4] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
)
As you can see key 0 is the same as 1, 3 and 4. And key 2 is different from them all.
When running the array_unique function on them, the only left is
Array (
[0] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
)
Any ideas why array_unique isn't working as expected?
It's because array_unique compares items using a string comparison. From the docs:
Note: Two elements are considered
equal if and only if (string) $elem1
=== (string) $elem2. In words: when the string representation is the same.
The first element will be used.
The string representation of an array is simply the word Array, no matter what its contents are.
You can do what you want to do by using the following:
$arr = array(
array('user_id' => 33, 'frame_id' => 3),
array('user_id' => 33, 'frame_id' => 3),
array('user_id' => 33, 'frame_id' => 8)
);
$arr = array_intersect_key($arr, array_unique(array_map('serialize', $arr)));
//result:
array
0 =>
array
'user_id' => int 33
'user' => int 3
2 =>
array
'user_id' => int 33
'user' => int 8
Here's how it works:
Each array item is serialized. This
will be unique based on the array's
contents.
The results of this are run through array_unique,
so only arrays with unique
signatures are left.
array_intersect_key will take
the keys of the unique items from
the map/unique function (since the source array's keys are preserved) and pull
them out of your original source
array.
Here's an improved version of #ryeguy's answer:
<?php
$arr = array(
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
array('user_id' => 33, 'tmp_id' => 5)
);
# $arr = array_intersect_key($arr, array_unique(array_map('serialize', $arr)));
$arr = array_intersect_key($arr, array_unique(array_map(function ($el) {
return $el['user_id'];
}, $arr)));
//result:
array
0 =>
array
'user_id' => int 33
'tmp_id' => int 3
First, it doesn't do unneeded serialization. Second, sometimes attributes may be different even so id is the same.
The trick here is that array_unique() preserves the keys:
$ php -r 'var_dump(array_unique([1, 2, 2, 3]));'
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[3]=>
int(3)
}
This let's array_intersect_key() leave the desired elements.
I've run into it with Google Places API. I was combining results of several requests with different type of objects (think tags). But I got duplicates, since an object may be put into several categories (types). And the method with serialize didn't work, since the attrs were different, namely, photo_reference and reference. Probably these are like temporary ids.
array_unique() only supports multi-dimensional arrays in PHP 5.2.9 and higher.
Instead, you can create a hash of the array and check it for unique-ness.
$hashes = array();
foreach($array as $val) {
$hashes[md5(serialize($val))] = $val;
}
array_unique($hashes);
array_unique deosn't work recursive, so it just thinks "this are all Arrays, let's kill all but one... here we go!"
Quick Answer (TL;DR)
Distinct values may be extracted from PHP Array of AssociativeArrays using foreach
This is a simplistic approach
Detailed Answer
Context
PHP 5.3
PHP Array of AssociativeArrays (tabluar composite data variable)
Alternate name for this composite variable is ArrayOfDictionary (AOD)
Problem
Scenario: DeveloperMarsher has a PHP tabular composite variable
DeveloperMarsher wishes to extract distinct values on a specific name-value pair
In the example below, DeveloperMarsher wishes to get rows for each distinct fname name-value pair
Solution
example01 ;; DeveloperMarsher starts with a tabluar data variable that looks like this
$aodtable = json_decode('[
{
"fname": "homer"
,"lname": "simpson"
},
{
"fname": "homer"
,"lname": "jackson"
},
{
"fname": "homer"
,"lname": "johnson"
},
{
"fname": "bart"
,"lname": "johnson"
},
{
"fname": "bart"
,"lname": "jackson"
},
{
"fname": "bart"
,"lname": "simpson"
},
{
"fname": "fred"
,"lname": "flintstone"
}
]',true);
example01 ;; DeveloperMarsher can extract distinct values with a foreach loop that tracks seen values
$sgfield = 'fname';
$bgnocase = true;
//
$targfield = $sgfield;
$ddseen = Array();
$vout = Array();
foreach ($aodtable as $datarow) {
if( (boolean) $bgnocase == true ){ #$datarow[$targfield] = #strtolower($datarow[$targfield]); }
if( (string) #$ddseen[ $datarow[$targfield] ] == '' ){
$rowout = array_intersect_key($datarow, array_flip(array_keys($datarow)));
$ddseen[ $datarow[$targfield] ] = $datarow[$targfield];
$vout[] = Array( $rowout );
}
}
//;;
print var_export( $vout, true );
Output result
array (
0 =>
array (
0 =>
array (
'fname' => 'homer',
'lname' => 'simpson',
),
),
1 =>
array (
0 =>
array (
'fname' => 'bart',
'lname' => 'johnson',
),
),
2 =>
array (
0 =>
array (
'fname' => 'fred',
'lname' => 'flintstone',
),
),
)
Pitfalls
This solution does not aggregate on fields that are not part of the DISTINCT operation
Arbitrary name-value pairs are returned from arbitrarily chosen distinct rows
Arbitrary sort order of output
Arbitrary handling of letter-case (is capital A distinct from lower-case a ?)
See also
php array_intersect_key
php array_flip
function array_unique_recursive($array)
{
$array = array_unique($array, SORT_REGULAR);
foreach ($array as $key => $elem) {
if (is_array($elem)) {
$array[$key] = array_unique_recursive($elem);
}
}
return $array;
}
Doesn't that do the trick ?
`
$arr = array(
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
);
$arr1 = array_unique($arr,SORT_REGULAR);
echo "<pre>";
print_r($arr1);
echo "</pre>";
Array(
[0] => Array(
[user_id] => 33
[tmp_id] => 3
)
[1] => Array(
[user_id] => 33
[tmp_id] => 4
)
)
`

Categories