Nested array. Third level is disappearing - php

I have that array :
$a = array(
"7" => array(
"id" => 7,
"parent" => 6
),
"6" => array(
"id" => 6,
"parent" => 5
),
"5" => array(
"id" => 5,
"parent" => 4
),
"4" => array(
"id" => 4,
"parent" => 0
),
"3" => array(
"id" => 7,
"parent" => 2
),
"2" => array(
"id" => 7,
"parent" => 1
),
"1" => array(
"id" => 7,
"parent" => 0
)
);
the result that I want is that :
$a = array(
"4" => array(
"id" => 4,
"parent" => 0,
array(
"5" => array(
"id" => 5,
"parent" => 4,
array(
"6" => array(
"id" => 6,
"parent" => 5,
array(
"7" => array(
"id" => 7,
"parent" => 6
)
)
)
)
)
)
),
"2" => array(
"id" => 7,
"parent" => 1,
array(
"3" => array(
"id" => 7,
"parent" => 2
)
)
),
"1" => array(
"id" => 7,
"parent" => 0
)
);
the code that I use is this :
foreach($a as $v)
{
if(isset($a[$v['PARENT']]))
{
$a[$v['PARENT']][$v['ID']] = $v;
unset($a[$v['ID']]);
}
}
and the problem that I have is that I get that result :
$a = array(
"4" => array(
"id" => 4,
"parent" => 0,
array(
"5" => array(
"id" => 5,
"parent" => 4
)
)
),
"2" => array(
"id" => 7,
"parent" => 1,
array(
"3" => array(
"id" => 7,
"parent" => 2
)
)
),
"1" => array(
"id" => 7,
"parent" => 0
)
);
instead of the need it result.

To solve your problem you need to properly understand how variable referencing/aliasing in PHP works.
Look at the following example code, which does not look much different to yours but makes use of references in order to access any parent even it has already "moved":
# transform $flat into a tree:
foreach($flat as $id => &$value)
{
# check if there is a parent
if ($parentId = $value['parent'])
{
$flat[$parentId][0][$id] =& $value; # add child to parent
unset($flat[$id]); # remove reference from topmost level
}
}
unset($value); # remove iterator reference
print_r($flat); # your tree
$flat now contains all values from $flat - but reordered. Demo.

Are you sure that output array is correct? Surely the key 2 should be a child of 1 (since 2 has 'parent'=>1)? If this is not the case, I don't understand what are actually trying to do and how the keys all relate to each other.
If 2 should be a child of 1, this works:
$keep = array();
foreach ($a as $k => &$v) {
// Loop the array first time and create references to
// structure the array how you want it
if ($v['parent']) {
$a[$v['parent']][0] = array($k => &$v);
} else $keep[] = $k;
}
foreach ($a as $k => $v) {
// Loop it again to get rid of non-root nodes from the root
if (!in_array($k,$keep)) {
unset($a[$k]);
}
}
print_r($a);

Related

Remove two dimentional array elements(except first match) which array specific value are same/unique

For example, I have an following two dimentional array is below,
$a = array(
"one" => array (
"id" => 111,
"name" => "Jhon"
),
"two" => array(
"id" => 222,
"name" => "Adam"
),
"three" => array(
"id" => 111,
"name" => "Mark"
),
"four" => array(
"id" => 125,
"name" => "Jhon"
),
"five" => array(
"id" => 111,
"name" => "Jhon"
),
"six" => array(
"id" => 222,
"name" => "Rock"
),
);
I would like to remove array(except first match) which two dimensional array values are same/unique.
For example, I would like to remove all of the arrays(except first match) which id keys values are same.
As, array id keys values 111(has array count 3) and 222(has array count 2).
So after removing the unique key id the result array should be following,
$a = array(
"one" => array (
"id" => 111,
"name" => "Jhon"
),
"two" => array(
"id" => 222,
"name" => "Adam"
),
"four" => array(
"id" => 125,
"name" => "Jhon"
),
);
You need to keep an array with added keys. You could use array_filter() to remove unnecessary rows.
Code: (demo)
$array = [
"one" => ["id" => 111, "name" => "Jhon"],
"two" => ["id" => 222, "name" => "Adam"],
"three" => ["id" => 111, "name" => "Mark"],
"four" => ["id" => 125, "name" => "Jhon"],
"five" => ["id" => 111, "name" => "Jhon"],
"six" => ["id" => 222, "name" => "Rock"],
];
// store added IDs
$added = [];
// filter array (need to pass $added as reference to be updated)
$output = array_filter($array, function($item) use (&$added)
{
// shortcut for readability
$id = $item['id'];
// Check if id already exists
if (isset($added[$id])) { return false; }
// add to reference
$added[$id] = true;
// add to final array
return true;
});
unset($added); // no longer needed.
var_dump($output);
Output:
array(3) {
["one"]=>
array(2) {
["id"]=>
int(111)
["name"]=>
string(4) "Jhon"
}
["two"]=>
array(2) {
["id"]=>
int(222)
["name"]=>
string(4) "Adam"
}
["four"]=>
array(2) {
["id"]=>
int(125)
["name"]=>
string(4) "Jhon"
}
}
Use this code to complete your task
<?php
$a = array(
"one" => array (
"id" => 111,
"name" => "Jhon"
),
"two" => array(
"id" => 222,
"name" => "Adam"
),
"three" => array(
"id" => 111,
"name" => "Mark"
),
"four" => array(
"id" => 125,
"name" => "Jhon"
),
"five" => array(
"id" => 111,
"name" => "Jhon"
),
"six" => array(
"id" => 222,
"name" => "Rock"
),
);
$unique = [];
$final_array = [];
foreach ($a as $value) {
if(in_array($value['id'],$unique)){
continue;
}
else {
$unique[] = $value['id'];
$final_array[] = $value;
}
}
echo "<pre>";
print_r($final_array);
?>
Here i use a unique named array in which i put the unique key and loop through the array if id exsit in unique then it skip that otherwise i add that in final array.
Hope this will sort your problem.

Recursively getting a list of parents for each node in a nested PHP array

In PHP I have a set of nested arrays of data. I want to flatten these, so that for every node and leaf I get a list of its parents.
Here's an example of the original array - note, each part could be of an unknown depth and length:
$data = array(
"name" => "Thing",
"children" => array(
array(
"name" => "Place",
"rdfs:subClassOf" => "schema:Thing",
"children" => array(
array(
"name" => "Accomodation",
"rdfs:subClassOf" => "schema:Place",
"children" => array(
array(
"name" => "Apartment",
"rdfs:subClassOf" => "schema:Accomodation",
),
array(
"name" => "Hotel",
"rdfs:subClassOf" => "schema:Accomodation",
),
array(
"name" => "House",
"rdfs:subClassOf" => "schema:Accomodation",
)
)
)
)
),
array(
"name" => "CreativeWork",
"rdfs:subClassOf" => "schema:Thing",
"children" => array(
array(
"name" => "Article",
"rdfs:subClassOf" => "schema:CreativeWork",
"children" => array(
array(
"name" => "NewsArticle",
"rdfs:subClassOf" => "schema:Article",
),
array(
"name" => "OpinionArticle",
"rdfs:subClassOf" => "schema:Article",
),
)
)
)
)
)
);
(The data I'm actually parsing is this Schema.org JSON file - the above is a minimal example of it.)
And here's what I'd like to end up with:
$results = array(
"Place" => array("Thing"),
"Accomodation" => array("Thing", "Place"),
"Apartment" => array("Thing", "Place", "Accomodation"),
"Hotel" => array("Thing", "Place", "Accomodation"),
"House" => array("Thing", "Place", "Accomodation"),
"CreativeWork" => array("Thing"),
"Article" => array("Thing", "CreativeWork"),
"NewsArticle" => array("Thing", "CreativeWork", "Article"),
"OpinionArticle" => array("Thing", "CreativeWork", "Article"),
);
I'm assuming I need to recursively call a function to build the array but so far I'm not having much luck. In case this makes it harder, this is happening in a static method.
Something quick to get you started:
class Parser {
public static function parse($input,$prefix = []) {
$return = $prefix ? [$input['name']=>$prefix] : [];
if (isset($input['children'])) {
$prefix[] = $input['name'];
foreach ($input['children'] as $child) {
$return += self::parse($child,$prefix);
}
}
return $return;
}
}
var_dump(Parser::parse($data));
You probably need to add a few checks and comments to make it more readable.

Convert PHP Array from One to Multi-dimensional Based on Parent ID Values

I've got a one-dimensional array of objects that represent multi-dimensional data:
array(
array(
"id" => 1,
"parent_id" => 0,
"content" => 'des'
),
array(
"id" => 2,
"parent_id" => 3,
"content" => 'abc'
),
array(
"id" => 3,
"parent_id" => 1,
"content" => 'jjjj'
),
array(
"id" => 4,
"parent_id" => 5,
"content" => 'dsfsd'
),
array(
"id" => 5,
"parent_id" => 0,
"content" => 'dsfsd'
)
);
How should I convert it into a multi-dimensional array?
array(
array(
"id" => 1,
"parent_id" => 0,
"content" => 'des'
),
array(
"id" => 3,
"parent_id" => 1,
"content" => 'jjjj'
),
array(
"id" => 2,
"parent_id" => 3,
"content" => 'abc'
),
array(
"id" => 5,
"parent_id" => 0,
"content" => 'dsfsd'
),
array(
"id" => 4,
"parent_id" => 5,
"content" => 'dsfsd'
)
);
I'd like to sort by id and children after the parent, if parent_id = 0 it is the root element. Thanks everybody very much!
I think you need to sort the array by parent_id.
array_sort() a laravel helper function : https://laravel.com/docs/5.7/helpers#method-array-sort
$sorted = array_sort($array, 'parent_id');
output
array:5 [▼
0 => array:3 [▼
"id" => 1
"parent_id" => 0
"content" => "des"
]
4 => array:3 [▼
"id" => 5
"parent_id" => 0
"content" => "dsfsd"
]
2 => array:3 [▼
"id" => 3
"parent_id" => 1
"content" => "jjjj"
]
1 => array:3 [▼
"id" => 2
"parent_id" => 3
"content" => "abc"
]
3 => array:3 [▼
"id" => 4
"parent_id" => 5
"content" => "dsfsd"
]
]
this is my code (done)
function sort($array){
$newArray = [];
sortLoop($array, $newArray);
return $newArray;
}
function sortLoop($array, &$newArray, $parent_id = 0){
foreach ($array as $key => $item) {
if ($item['parent_id'] == $parent_id) {
$newArray[] = $item;
unset($array[$key]);
sortLoop($array,$newArray, $item['id']);
}
}
}
Wish this useful for someone.

PHP array merge / map of two arrays

I have two arrays, one that contains all options, and a second one that contains the default values.
The options arrays looks like this:
$options = array(
"SeriesA" => array(
"A1" => array(
"text" => "A1",
"value" => "A-001"
),
"A2" => array(
"text" => "A2",
"value" => "A-002"
)
),
"SeriesB" => array(
"B1" => array(
"text" => "B2",
"value" => "B-001"
),
"B2" => array(
"text" => "B2",
"value" => "B-002"
)
),
);
And I have another array that contains default value, and it looks like this
$defaults= array(
"SeriesA" => "A-002",
"SeriesB" => "B-001",
);
What I would like to end up with is one array that contains all info,
is there a way that I can map both arrays and get one array that will look like this:
$options = array(
"SeriesA" => array(
"A1" => array(
"text" => "A1",
"value" => "A-001",
"default" => false
),
"A2" => array(
"text" => "A2",
"value" => "A-002",
"default" => true
)
),
"SeriesB" => array(
"B1" => array(
"text" => "B2",
"value" => "B-001",
"default" => true
),
"B2" => array(
"text" => "B2",
"value" => "B-002",
"default" => false
)
),
);
Here is two ways to do it:
Make a function, which accepts two args and check value in a loop with defaults, add defaults, and returns new array, or edit array, passing it by reference:
function awesomeName(&$options, $defaults) {
foreach ($options as $k => &$values) {
foreach ($values as &$AsAndBs) {
$AsAndBs['default'] = $AsAndBs['value'] == $defaults[$k];
}
}
}
Using array_walk() function with anonymous function:
array_walk($options, function (&$v, $k) use ($defaults) {
$series = $k;
foreach ($v as &$series_contents) {
$series_contents['default'] = $series_contents['value'] == $defaults[$series];
}
});

Populate multidimensional array's rank column with dense rank number

My array structure is as follows -
[
[
"points" => 10,
"details" => ["name" => "Team A", "rank" => ""]
],
[
"points" => 10,
"details" => ["name" => "Team B", "rank" => ""]
],
[
"points" => 8,
"details" => ["name" => "Team C", "rank" => ""]
],
[
"points" => 6,
"details" => ["name" => "Team D", "rank" => ""]
],
]
Now I want populate the array's "rank" value with the appropriate dense rank. Expected result:
[
[
"points" => 10,
"details" => ["name" => "Team A", "rank" => 1]
],
[
"points" => 10,
"details" => ["name" => "Team B", "rank" => 1]
],
[
"points" => 8,
"details" => ["name" => "Team C", "rank" => 2]
],
[
"points" => 6,
"details" => ["name" => "Team D", "rank" => 3]
],
]
How can I achieve this output? I tried looping through each element in the array and comparing points, but I didn't find that to be really efficient.
How about to create another array and store desired result there
$array = array(
array(
"points" => 10,
"details" => array(
"name" => "Team A",
"rank" => ""
)
),
array(
"points" => 11,
"details" => array(
"name" => "Team B",
"rank" => ""
)
)
);
$c = 0; // count identifier
$n = array(); // create new array
for ($i=0;$i<count($array);$i++){ // loop through each array
foreach ($array[$i] as $value){ // loop through into sub arrays
if (is_array($value)){
$n[$i]['details'] = array(
"name" => $value['name'],
"rank" => $c
);
$c++;
} else {
$n[$i]['points'] = $value;
}
}
}
print_r($n);
Output will be:
Array ( [0] => Array ( [points] => 10 [details] => Array ( [name] => Team A [rank] => 0 ) ) [1] => Array ( [points] => 11 [details] => Array ( [name] => Team B [rank] => 1 ) ) )
A bit bruteforce but it should work.
$array = array(
array(
"points" => 10,
"details" => array(
"name" => "Team A",
"rank" => ""
)
),
array(
"points" => 11,
"details" => array(
"name" => "Team B",
"rank" => ""
)
),
array(
"points" => 10,
"details" => array(
"name" => "Team A",
"rank" => ""
)
),
array(
"points" => 11,
"details" => array(
"name" => "Team B",
"rank" => ""
)
)
);
$points = array();
foreach($array as $key => $arr){
$points[] = $arr['points'];
}
asort($points);
foreach($points as $pkey => $point){
foreach($array as $akey => $arr){
if($point == $arr['points']){
$array[$akey]['details']['rank'] = $pkey+1;
}
}
}
var_dump($array);
Since your multidimensional input array is already sorted descending by the points column, you can loop through the array and modify its rank data by reference while conditionally incrementing the rank variable.
Code: (Demo)
$denseRank = 0;
foreach ($array as ['points'=> $points, 'details' => ['rank' => &$rank]]) {
$denseRanks[$points] ??= ++$denseRank;
$rank = $denseRanks[$points];
}
var_export($array);
The above snippet uses "array destructuring" in the foreach() to declare only used variables; it is just as viable to declare &$row and then access its elements in the loop's body.

Categories