Array manipulation (PHP) - php

I have an array like this one :
Array
{
'property1.subproberty11' => "xxxxx",
'property1.subproberty12' => "yyyy",
'property2.subproberty21.subproperty211' => "zzzzzz",
'property2.subproberty21.subproperty212' => "wwwww",
'property2.subproberty22' => "yyyy",
....
That needs to be changed into something like :
Array
(
[property1] => Array
(
['subproberty11'] => "xxxxx"
['subproberty12'] => "yyyy"
)
[property2] => Array
(
[subproperty21] => Array
(
[subproperty211] => "zzzzzz"
[subproperty212] => "wwwwww"
)
['subproberty22'] => "yyyy"
)
...
I can't find a smart way of doing this, can someone help me ?
So, far, i have thought of this kind of algorithm :
$new_array = array();
foreach($old_array as $key => $value)
{
$subkeys = explode('.', $key);
$ss = array();
for($ii = 0 ; $ii < count($subkeys) ; $ii++)
{
$ss[] = "['".$subkeys[$ii]."']";
if ($ii < count($subkeys) -1)
eval('$new_array'.implode('',$ss).' = array();');
}
eval('$new_array'.implode('',$ss)." = '".$value."';');
}
I think we can do better, for example maybe we can avoid duplicating data by creating a new array ?

My working example:
function nestedKeysArray($input) {
$array = array();
foreach ($input as $key => $value) {
$keys = explode('.', $key);
if (count($keys) == 1) {
$array[$key] = $value;
} else {
$nested = &$array;
foreach ($keys as $k) {
if (!isset($nested[$k]))
$nested[$k] = array();
$nested = &$nested[$k];
}
$nested = $value;
}
}
return $array;
}
$input is array like first one from the question.
EDIT:
Changing original array, without copy:
function nestedKeysArray(&$input) {
foreach ($input as $key => $value) {
$keys = explode('.', $key);
if (count($keys) > 1) {
$nested = &$input;
foreach ($keys as $k) {
if (!isset($nested[$k]))
$nested[$k] = array();
$nested = &$nested[$k];
}
$nested = $value;
unset($input[$key]);
}
}
}

Some untested code, to give you the direction you could take.
Just loop through the array;
function SplitArray($properties) {
foreach($properties as $item=>$property) {
$properties[$item] = explode('.',$property, 2);
if(strpos($properties[$item][1], '.') === false)) {}
else {
$properties[$item][1] = SplitArray($properties[$item][1]);
}
}
return $properties;
}

Related

Convert flat array with dot-delimited keys to nested array

I am trying to created nested array from flat based on its keys.
Also format of keys in original array can be changed if it will simplify task.
From :
$arr = [
'player.name' => 'Joe',
'player.lastName' => 'Snow',
'team.name' => 'Stars',
'team.picture.name' => 'Joe Snow Profile',
'team.picture.file' => 'xxx.jpg'
];
To:
$arr = [
'player' => [
'name' => 'Joe'
, 'lastName' => 'Snow'
]
,'team' => [
'name'=> 'Stars'
,'picture' => [
'name' => 'Joe Snow Profile'
, 'file' =>'xxx.jpg'
]
],
];
Here is my take on it.
It should be able to handle arbitrary depth
function unflatten($arr) {
$result = array();
foreach($arr as $key => $value) {
$keys = explode(".", $key); //potentially other separator
$lastKey = array_pop($keys);
$node = &$result;
foreach($keys as $k) {
if (!array_key_exists($k, $node))
$node[$k] = array();
$node = &$node[$k];
}
$node[$lastKey] = $value;
}
return $result;
}
Combination of iteration and recursion. Could be simplified to just iterative.
$array = [
'player.name' => 'Joe',
'player.lastName' => 'Snow',
'team.name' => 'Stars',
'team.picture.name' => 'Joe Snow Profile',
'team.picture.file' => 'xxx.jpg'
];
$newArray = array ();
foreach($array as $key=> $value) {
$temp = array ();
$keys = array_reverse (explode('.', $key));
$temp[$keys[0]] = $value;
for ($i = 1; $i < count($keys); $i++) {
$temp[$keys[$i]] = $temp;
unset ($temp [$keys [$i -1]]);
}
$newArray = array_merge_recursive($newArray,$temp);
}
var_dump($newArray );
I received this question as a test, this is my answer:
<?php
function buildArray(&$newArray, $keys, $value)
{
if (sizeof($keys) == 0) {
return $value;
} else {
$key = array_shift($keys);
if (isset($newArray[$key])) {
$value = buildArray($newArray[$key], $keys, $value);
if (is_array($value)) {
$newArray[$key] += $value;
} else {
$newArray[$key] = $value;
}
$arr = $newArray;
} else {
$arr[$key] = buildArray($newArray, $keys, $value);
}
}
return $arr;
}
$arr = [
'player.name' => 'Joe',
'player.lastName' => 'Snow',
'team.name' => 'Stars',
'team.picture.name' => 'Joe Snow Profile',
'team.picture.file' => 'xxx.jpg',
];
$newArray = [];
foreach ($arr as $key => $value) {
$explodedKey = explode(".", $key);
$temp = buildArray($newArray, $explodedKey, $value);
$newArray = array_merge($temp, $newArray);
}
print_r($newArray);
?>
You could do it like this
$newArr = [];
for ($arr as $key => $val) {
$tmp = explode ('.', $key);
if (!array_key_exists ($tmp [0], $newArray){
$newArray [$tmp [0]] = [];
}
$newArray [$tmp [0]][$tmp [1]] = $val;
}
edit:
Damn didn't saw the third level in team.
Not very generic but should work for third level ;)
$newArr = [];
for ($arr as $key => $val) {
$tmp = explode ('.', $key);
if (!array_key_exists ($tmp [0], $newArray){
$newArray [$tmp [0]] = [];
}
if (count($tmp) > 2){
if (!array_key_exists ($tmp [1], $newArray[$tmp [0]]){
$newArray [$tmp [0]][$tmp[1]] = [];
}
$newArray [$tmp [0]][$tmp [1]][$tmp [2]] = $val;
} else {
$newArray [$tmp [0]][$tmp [1]] = $val;
}
}
I think you can use something like this, for converting 2d array to nested tree. But You'll have to play with parent_id
https://github.com/denis-cto/flat-array-to-nested-tree

get rid of arrays into an array in php

I came up with this ugly verbose array (and I have to use it):
$puppy_mother_father_arr = array(
array('46' => array('30','29')),
array('17' => array('30','29')),
array('16' => array('24','29'))
);
How do I simplify it to something like this :
$puppy_mother_father_arr = array(
'46' => array('30','29'),
'17' => array('30','29'),
'16' => array('24','29')
);
I've been stuck a day in here. thanks in advance
$tmp = array();
foreach ($puppy_mother_father_arr as $parent) {
foreach($parent as $key => $nodes) {
$tmp[$key] = $nodes;
}
}
$puppy_mother_father_arr = $tmp;
Would this work?
See the result online
<?php
$puppy_mother_father_arr = array( array('46' => array('30','29')),array('17' => array('30','29')),array('16' => array(24,'29')) );
$list = array();
foreach ($puppy_mother_father_arr as $info)
{
foreach ($info as $key => $value)
{
$list[$key] = $value;
break;
}
}
var_export($list);
$newarray = array();
foreach ($puppy_mother_father_arr as $array) {
foreach ($array as $puppy => $parents) {
$newarray[$puppy] = $parents;
}
}
$puppy_mother_father_arr = $newarray;
key and current could be useful if each array has only one interested key and value:
$result = array();
foreach($puppy_mother_father_arr as $arr) {
$result[key($arr)] = current($arr);
}
var_dump($result);

Is there something like keypath in an associative array in PHP?

I want to dissect an array like this:
[
"ID",
"UUID",
"pushNotifications.sent",
"campaigns.boundDate",
"campaigns.endDate",
"campaigns.pushMessages.sentDate",
"pushNotifications.tapped"
]
To a format like this:
{
"ID" : 1,
"UUID" : 1,
"pushNotifications" :
{
"sent" : 1,
"tapped" : 1
},
"campaigns" :
{
"boundDate" : 1,
"endDate" : 1,
"pushMessages" :
{
"endDate" : 1
}
}
}
It would be great if I could just set a value on an associative array in a keypath-like manner:
//To achieve this:
$dissected['campaigns']['pushMessages']['sentDate'] = 1;
//By something like this:
$keypath = 'campaigns.pushMessages.sentDate';
$dissected{$keypath} = 1;
How to do this in PHP?
You can use :
$array = [
"ID",
"UUID",
"pushNotifications.sent",
"campaigns.boundDate",
"campaigns.endDate",
"campaigns.pushMessages.sentDate",
"pushNotifications.tapped"
];
// Build Data
$data = array();
foreach($array as $v) {
setValue($data, $v, 1);
}
// Get Value
echo getValue($data, "campaigns.pushMessages.sentDate"); // output 1
Function Used
function setValue(array &$data, $path, $value) {
$temp = &$data;
foreach(explode(".", $path) as $key) {
$temp = &$temp[$key];
}
$temp = $value;
}
function getValue($data, $path) {
$temp = $data;
foreach(explode(".", $path) as $ndx) {
$temp = isset($temp[$ndx]) ? $temp[$ndx] : null;
}
return $temp;
}
function keyset(&$arr, $keypath, $value = NULL)
{
$keys = explode('.', $keypath);
$current = &$arr;
while(count($keys))
{
$key = array_shift($keys);
if(!isset($current[$key]) && count($keys))
{
$current[$key] = array();
}
if(count($keys))
{
$current = &$current[$key];
}
}
$current[$key] = $value;
}
function keyget($arr, $keypath)
{
$keys = explode('.', $keypath);
$current = $arr;
foreach($keys as $key)
{
if(!isset($current[$key]))
{
return NULL;
}
$current = $current[$key];
}
return $current;
}
//Testing code:
$r = array();
header('content-type: text/plain; charset-utf8');
keyset($r, 'this.is.path', 39);
echo keyget($r, 'this.is.path');
var_dump($r);
It's a little rough, I can't guarantee it functions 100%.
Edit: At first you'd be tempted to try to use variable variables, but I've tried that in the past and it doesn't work, so you have to use functions to do it. This works with some limited tests. (And I just added a minor edit to remove an unnecessary array assignment.)
In the meanwhile, I came up with (another) solution:
private function setValueForKeyPath(&$array, $value, $keyPath)
{
$keys = explode(".", $keyPath, 2);
$firstKey = $keys[0];
$remainingKeys = (count($keys) == 2) ? $keys[1] : null;
$isLeaf = ($remainingKeys == null);
if ($isLeaf)
$array[$firstKey] = $value;
else
$this->setValueForKeyPath($array[$firstKey], $value, $remainingKeys);
}
Sorry for the "long" namings, I came from the Objective-C world. :)
So calling this on each keyPath, it actually gives me the output:
fields
Array
(
[0] => ID
[1] => UUID
[2] => pushNotifications.sent
[3] => campaigns.boundDate
[4] => campaigns.endDate
[5] => campaigns.pushMessages.endDate
[6] => pushNotifications.tapped
)
dissectedFields
Array
(
[ID] => 1
[UUID] => 1
[pushNotifications] => Array
(
[sent] => 1
[tapped] => 1
)
[campaigns] => Array
(
[boundDate] => 1
[endDate] => 1
[pushMessages] => Array
(
[endDate] => 1
)
)
)

php select index where array contains

is there a function out there to search in a array if it contains a part of a text
just like the jquery(':contains')
and then return the index it is in :)
here is an example to help you visualise it :)
$arr = array(
[0] => 'hello world',
[1] => 'foo',
[2] => 'bar',
);
$a = arr_contains('o',$arr); //returns array(1,0);
$b = arr_contains('fo',$arr);//return array(1);
$c = arr_contains('a',$arr);//return array(2);
$d = arr_contains('hello',$arr);//return array(0);
if recursively can be done would be a plus :)
Nope, you will have to write a custom function for matching by substring:
function arr_contains($str, $arr) {
$ret = array();
foreach ($arr as $k => $v) {
if (is_array($v)) {
if ($subarr = arr_contains($str, $v)) {
$ret[] = $subarr;
}
} else if (strpos($v, $str) !== false) {
$ret[] = $k;
}
}
return $ret;
}

How to merge arrays but no numerical keys in fewest lines in php?

$result = array_merge($arr1,$arr2);
I want to exclude numerical values of $arr2,is there an option for this?
Edit after comment:
$arr1 = array('key' => 1);
$arr2 = array('test',1 => 'test', 'key2' => 2);
after processing I need the result to be:
array('key' => 1,'key2' => 2);
Excluding numerical keys
It seems that you want to array_filter your $arr2's keys, first:
function not_numeric( $object ) {
return !is_numeric( $object );
}
$no_numeric_keys = array_filter( array_keys( $arr2 ), not_numeric );
$no_numeric_array = array_intersect_keys( $arr2, $no_numeric_keys );
$result = array_merge( $arr1, $no_numeric_array );
I'm guessing that this would work, after using $result = array_merge($arr1,$arr2);:
foreach ($result as $key => $value) {
if (is_numeric($key)) {
unset($result[$key]);
}
}
Edit:
In as few lines as possible (1) – as requested in the new title:
foreach ($result as $key => $value) { if (is_numeric($key)) { unset($result[$key]); } }
Just loop through each array and test if keys are strings:
$output = array();
foreach($arr1 as $key => $value) {
if(is_string($key)) {
$output[$key] = $value;
}
}
foreach($arr2 as $key => $value) {
if(is_string($key)) {
$output[$key] = $value;
}
}
Edit:
Since you said elegant...
function merge_arrays_string_keys()
{
$output = array();
foreach(func_get_args() as $arr)
{
if(is_array($arr))
{
foreach($arr as $key => $value) {
if(is_string($key) {
$output[$key] = $value;
}
}
}
}
return $output;
}
elegant, huh?
$test = array('test', 1 => 'test', 'key2' => 2, 33, 3 => 33, 'foo' => 'bar');
$test_non_num = array_intersect_key(
$test,
array_flip(
array_diff(
array_keys($test),
array_filter(array_keys($test), 'is_int'))));
print_r($test_non_num); // key2=>2, foo=>bar
Use this code , it will also do the require thing.
<?php
$result = array ( 1,"pavunkumar","bks", 123 , "3" ) ;
array_walk($result,'test_print');
print_r ( $result ) ;
function test_print( $val , $key )
{
global $result;
if ( gettype ( $val ) == 'integer' )
{
unset ( $result[$key] ) ;
}
}
array_diff_ukey($m=$arr2+$arr1,$m,function($k){return is_string($k);})

Categories