merge many array based on a common key php - php

I wasn't so clear in my first question, so i deleted it and here is a reformulation;
I have those arrays:
$open = array(array("FAI1","34"),array("FAI2","34"),array("FAI3","34"));
$click = array(array("FAI2","52"),array("FAI1","68"),array("FAI3","99"));
$unsubscribe = array(array("FAI2","103"),array("FAI3","67"),array("FAI1","102"));
$def_sent = array(array("FAI1","34",24),array("FAI2","34",23),array("FAI3","34",27));
$SB = array(array("FAI2","103"),array("FAI3","67"),array("FAI1","102"));
$HB = array(array("FAI2","103"),array("FAI3","67"),array("FAI1","102"));
I searched for a function to merge them and get a result like this:
$result = array(array("FAI1",34,68,102,34,24,102,102)
,array("FAI2","34",23.....),
array("FAI3","34",27....));
and to do this, i used the function, in the php online documentation, and this is the function
function array_merge_recursive() {
$arrays = func_get_args();
$base = array_shift($arrays);
foreach ($arrays as $array) {
reset($base);
while (list($key, $value) = #each($array)) {
if (is_array($value) && #is_array($base[$key])) {
$base[$key] = array_merge_recursive($base[$key], $value);
} else {
$base[$key] = $value;
}
}
}
return $base;
}
But instead of getting the result above i got this:
FAI1|34
FAI2|34
FAI3|34
FAI2|52
FAI1|68
FAI3|99
...
So i need some help to reformulate this function to get the expected result.

Try this function:
function array_merge_rec() {
$arrays = func_get_args();
$result = array();
foreach ($arrays as $arg) {
if (is_array($arg)) {
foreach ($arg as $item) {
if (!isset($result[$item[0]])) {
$result[$item[0]] = $item;
} else {
$result[$item[0]][] = $item[1];
}
}
} else {
echo "$arg skippend because it isn't array\n";
}
}
return array_values($result);
}
Does it help?

Related

Flattening multidimensional array created by json_decode

I'm trying to flatten a multidimensional array that was returned by json_decode() but I'm having issues. I've some research but all of the solutions seem to be skipping over some of my data. If I run this and compare the echo'd data to var_dump() I'm definitely not getting everything and I'm not sure why.
Here is what I have so far:
<?php
function array_flatten($array) {
if (!is_array($array)) {
return false;
}
$result = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$result = array_merge($result, array_flatten($value));
} else {
$result[$key] = $value;
}
}
return $result;
}
for ($x = 1; $x <= 1; $x++) {
$response = file_get_contents('https://seeclickfix.com/api/v2/issues?page='.$x
.'&per_page=1');
// Decode the JSON and convert it into an associative array.
$jsonDecoded = json_decode($response, true);
$flat = array_flatten($jsonDecoded['issues']);
foreach($flat as $item) {
echo $item;
echo "<br>";
}
}
?>
array_merge will overwrite values with the same key, as you can see in the documentation. E.g. in the link you posted, you will lose some urls. You could fix that by create unique keys in the flattened array. For example by passing a prefix to your function:
function array_flatten($array, $prefix = '') {
if (!is_array($array)) {
return false;
}
$result = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$result = array_merge($result, array_flatten($value, $prefix.'_'.$key));
} else {
$result[$prefix.'_'.$key] = $value;
}
}
return $result;
}

Concatenating func_get_args into a multidimentional array

I ran into this question on a different site, after attempting to work on it for an hour (could be my Sunday brains) I gave up. The question is: If there is a function foo:
function foo(){}
The function can be called as (arguments can be >= 2, where the last is always the value and the previous are part of the array).
So calling the function as:
foo('arg1', 'value');
Should result in:
$array['arg1'] = 'value';
The same if it has more than 1 argument:
foo('arg1', 'argx', 'argz', 'value');
Should produce:
$array['arg1']['argx']['argz'] = 'value';
This was my sad attempt:
function foo()
{
$items = func_get_args();
$value = array_pop($items);
$array = array_shift($items);
// Construct first element
$array = array($array => array());
foreach ($items as $el) {
insert_last($array, $value);
}
return $array;
}
function insert_last(&$array, $value)
{
$copy = $array;
while (true) {
$keys = array_keys($copy);
$last = $copy[$keys[count($copy)-1]];
var_dump($last);
if (empty($last)) {
$last = $value;
break;
}
$copy = $last;
}
var_dump($array, $copy);
}
Pretty sure there is probably an easier solution that I just can't think of at the moment. Thanks!
function foo()
{
$items = func_get_args();
$value = array_pop($items);
$array = [];
$arrayPtr = &$array;
foreach ($items as $element) {
$arrayPtr[$element] = null;
$arrayPtr = &$arrayPtr[$element];
}
$arrayPtr[$element] = $value;
return $array;
}
var_dump(foo('arg1', 'argx', 'argz', 'value'));
Demo
Via recursion using call_user_func_array()
<?php
function foo() {
$items = func_get_args();
if ( 1==count($items) ) {
return array_shift($items);
}
else {
$key = array_shift($items);
return array( $key=>call_user_func_array('foo', $items) );
}
}
var_dump(foo('arg1', 'argx', 'argz', 'value'));
edit: same thing without func_get_args() but using a variadic function
<?php
function foo(...$items) {
if ( 1==count($items) ) {
return array_shift($items);
}
else {
$key = array_shift($items);
return array( $key=>call_user_func_array('foo', $items) );
}
}
var_dump(foo('arg1', 'argx', 'argz', 'value'));
What about something like
function foo()
{
$args = func_get_args();
$items = array_pop($args);
foreach (array_reverse($args) as $item) {
$items = array($item => $items);
}
return $items;
}
var_dump(foo('arg1', 'argx', 'argz', 'value'));
Demo

Multi-imensional Array based on Keys

Is there a simple way to take a single-dimensional array and convert it to a multi-dimensional array based on the spaces, or any character(s), in the keys?
$arr['foo1'] = 'bar1';
$arr['foo2'] = 'bar2';
$arr['foo3 tier1' ] = 'bar3';
$arr['foo4 tier1' ] = 'bar4';
and turn it into
$arr['foo1'] = 'bar1';
$arr['foo2'] = 'bar2';
$arr['foo3']['tier1'] = 'bar3';
$arr['foo4']['tier1'] = 'bar4';
you can always do some sort of foreach loop
$newarr = array();
foreach ($arr as $key => $value) {
$output = explode(' ',$key,2);
if(count($output)) {
$newarr[$output[0]][$output[1]] = $value;
} else {
$newarr[$key] = $value;
}
}
That code will only work for one space but you can expand it to multiple spaces, something like
function pivot($arr,$delimeter) {
$return = array();
foreach ($arr as $key => $value) {
$output = explode($delimeter,$key,2);
if(count($output)) {
if(strpos($output[1],$delimeter) > 0) {
$return[$output[0]] = pivot(array($output[1]=>$value),$delimeter);
} else {
$return[$output[0]][$output[1]] = $value;
}
} else {
$return[$key] = $value;
}
}
return $return;
}

Is it possible for a variable to evaluate to a multi-dimensional PHP array index

Given a multidimensional array or dictionary $array.
And assuming that $array['foo']['bar']['baz'] = 'something';
Is there a way other than via an eval statement for me use the multi-dimentional index foo/bar/baz? (The use case is in creating the index dynamically i.e. The function does not know what /foo/bar/baz/ is).
The only way I could figure to do this was:
$item = testGetIndex($array, "'foo']['bar']['baz'");
function testGetIndex($array, $index) {
eval('$item = $array[' . $index . '];');
return $item;
}
Note:
I should mention that I do not want to search this array. This is a weird use case. I am being passed a very large multi dimensional array and it's ugly to have to use constructs like..
$array[foo][bar]..[baz] to make modifications to the array.
Blatently reusing my answer here:
function recurseKeys(array $keys,array $array){
$key = array_shift($keys);
if(!isset($array[$key])) return null;
return empty($keys) ?
$array[$key]:
recurseKeys($keys,$array[$key];
}
$values = recurseKeys(explode('/','foo/bar/baz'),$yourArray);
edit: as Jack pointed out, recursion is not needed:
function findByKey(array $keys,array $array){
while(!is_null($key = array_shift($keys))){
if(!isset($array[$key])) return null;
$array = $array[$key];
}
return $array;
}
$values = findByKey(explode('/','foo/bar/baz'),$yourArray);
To modify an array using a path:
function setPath(&$root, $path, $value)
{
$paths = explode('/', $path);
$current = &$root;
foreach ($paths as $path) {
if (isset($current[$path])) {
$current = &$current[$path];
} else {
return null;
}
}
return $current = $value;
}
$path = 'foo/bar/baz';
$root = array('foo' => array('bar' => array('baz' => 'something')));
setPath($root, $path, '123');
You can tweak the function to just return a reference to the element you wish to change:
function &getPath(&$root, $path)
{
$paths = explode('/', $path);
$current = &$root;
foreach ($paths as $path) {
if (isset($current[$path])) {
$current = &$current[$path];
} else {
return null;
}
}
return $current;
}
$x = &getPath($root, $path);
$x = 456; // $root['foo']['bar']['baz'] == 456
A simple loop can make it, like:
function get(array $array, $keys) {
$val = $array;
foreach (explode('/', $keys) as $part) {
if (!isset($val[$part])) {
return null;
}
$val = $val[$part];
}
return $val;
}
$array['foo']['bar']['baz'] = 'something';
echo get($array, 'foo/bar/baz');
http://ideone.com/vcRvXW
Edit:
For modification, just use references:
function set(array &$array, $keys, $value) {
$val = &$array;
foreach (explode('/', $keys) as $part) {
if (!isset($val[$part])) {
$val[$part] = array();
}
$val = &$val[$part];
}
$val = $value;
}
http://ideone.com/WUNhF6

PHP Flatten Array with multiple leaf nodes

What is the best way to flatten an array with multiple leaf nodes so that each full path to leaf is a distinct return?
array("Object"=>array("Properties"=>array(1, 2)));
to yield
Object.Properties.1
Object.Properties.2
I'm able to flatten to Object.Properties.1 but 2 does not get processed with recursive function:
function flattenArray($prefix, $array)
{
$result = array();
foreach ($array as $key => $value)
{
if (is_array($value))
$result = array_merge($result, flattenArray($prefix . $key . '.', $value));
else
$result[$prefix . $key] = $value;
}
return $result;
}
I presume top down will not work when anticipating multiple leaf nodes, so either need some type of bottom up processing or a way to copy array for each leaf and process (althought that seems completely inefficient)
function flatten(array $data, $separator = '.') {
$result = array();
$stack = array();
$path = null;
reset($data);
while (!empty($data)) {
$key = key($data);
$element = $data[$key];
unset($data[$key]);
if (is_array($element)) {
if (!empty($data)) {
$stack[] = array($data, $path);
}
$data = $element;
$path .= $key . $separator;
} else {
$result[$path . $key] = $element;
}
if (empty($data) && !empty($stack)) {
list($data, $path) = array_pop($stack);
}
}
return $result;
}
var_dump(flatten(array("Object"=>array("Properties"=>array(1, 2)))));
Output:
array(2) {
["Object.Properties.0"]=>
int(1)
["Object.Properties.1"]=>
int(2)
}
Use function flatMapAssoc() from Kdyby Framework:
$flattened= array();
flatMapAssoc($array, function ($value, $keys) use (&$flattened) {
$flattened[implode('.', $keys)] = $value;
});
/**
* #param array|\Traversable $array
* #param callable $callback
* #return array
*/
function flatMapAssoc($array, $callback)
{
$callback = callback($callback);
$result = array();
$walker = function ($array, $keys = array()) use (&$walker, &$result, $callback) {
foreach ($array as $key => $value) {
$currentKeys = $keys + array(count($keys) => $key);
if (is_array($value)) {
$walker($value, $currentKeys);
continue;
}
$result[] = $callback($value, $currentKeys);
}
return $result;
};
return $walker($array);
}
I would use a wrapper function to hide implementation details (the prefix parameter)
and added an if branch to test for empty arrays. At last, in case of simple leaf you should use the $value variable and not the $key one.
$x = array("Object"=>array("Properties"=>array(1, 2), "test"=>array(), "post"));
function flatten ($array) {
return flattenArray('',$array);
}
function flattenArray($prefix, $array) {
$result = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
if(count($value)) {
$result = array_merge($result, flattenArray($prefix."$key.", $value));
} else {
$result[] = "$prefix$key";
}
} else {
$result[] = "$prefix$value";
}
}
return $result;
}
echo join("\n", flatten($x));
If you want to mimic a tree structure, maybe you can use a different array structure. Something like this:
$y = array ("Object",
array("Properties", 1, 2),
"test",
"post"
);
and flattenArray becomes:
function flattenArray($prefix, $array) {
$result = array();
$prefix .=array_shift($array).'.';
foreach ($array as $value) {
if (is_array($value)) {
$result = array_merge($result, flattenArray($prefix, $value));
} else {
$result[] = "$prefix$value";
}
}
return $result;
}

Categories