SOLVED
Im trying to denormalization (if this is the right word) an array of items.
Each item has a qty. And if an item has f.e. qty of 2 then i want to add the same item (a copy) qty-times (minus one because 1st is already there).
Example:
$items = [
[
'name' => 'Foo',
'qty' => 2,
'items' => [
[
'name' => 'Bar',
'qty' => 2,
'items' => [
[
'name' => 'Baz',
'qty' => 2,
'items' => [],
],
],
],
],
],
];
$expected = [
[
'name' => 'Foo',
'qty' => 1,
'items' => [
[
'name' => 'Bar',
'qty' => 1,
'items' => [
[
'name' => 'Baz',
'qty' => 1,
'items' => [],
],
[
'name' => 'Baz',
'qty' => 1,
'items' => [],
],
],
],
[
'name' => 'Bar',
'qty' => 1,
'items' => [
[
'name' => 'Baz',
'qty' => 1,
'items' => [],
],
[
'name' => 'Baz',
'qty' => 1,
'items' => [],
],
],
],
],
],
[
'name' => 'Foo',
'qty' => 1,
'items' => [
[
'name' => 'Bar',
'qty' => 1,
'items' => [
[
'name' => 'Baz',
'qty' => 1,
'items' => [],
],
[
'name' => 'Baz',
'qty' => 1,
'items' => [],
],
],
],
[
'name' => 'Bar',
'qty' => 1,
'items' => [
[
'name' => 'Baz',
'qty' => 1,
'items' => [],
],
[
'name' => 'Baz',
'qty' => 1,
'items' => [],
],
],
],
],
],
];
Script (recursive test):
function func(array $items, array &$r)
{
foreach ($items as $item) {
for ($i = 1; $i <= $item['qty']; $i++) {
echo "{$item['name']}\r\n";
func($item['items'], $r);
}
}
}
$r = [];
func($items, $r);
file_put_contents('tmp/_test.php', "<?php return\r\n" . var_export($r, true) . ";");
Out:
Foo
Bar
Baz
Baz
Bar
Baz
Baz
Foo
Bar
Baz
Baz
Bar
Baz
Baz
So far so good.
But i cant get the items correctly sorted into $r.
Thanks for any help.
Sorry. Solution came to my mind as i re.read my own post.
I was stuck in references. But i just should have returned the items array.
Working script:
function func(array $items)
{
$_items = [];
$n = 0;
foreach ($items as $item) {
for ($i = 1; $i <= $item['qty']; $i++) {
echo "{$item['name']}\r\n";
$_items[$n] = $item;
$_items[$n]['qty'] = 1;
$_items[$n]['items'] = func($item['items']);
$n++;
}
}
return $_items;
}
$r = func($items);
file_put_contents('tmp/_test.php', "<?php return\r\n" . var_export($r, true) . ";");
You can compact your solution and eliminate the $n counter.
I am starting $i from 0 so that only < is needed to maintain the logic.
I have used ++$i instead of $i++ as a micro-optimization.
I have renamed the function and variables because I found it a little confusing with the variable names being so similar and the function not being descriptive.
I tested this with 2,2,2, 2,0,2,, 1,2,3, and 3,2,1 and my changes have not damaged the result. I think this is as tight as I can make it.
Code: (Demo)
function expand(array $array){
$expanded=[];
foreach($array as $sub){
for($i=0; $i<$sub['qty']; ++$i){
$expanded[]=['name'=>$sub['name'],'qty'=>1,'items'=>expand($sub['items'])];
}
}
return $expanded;
}
$items = [
[
'name' => 'Foo',
'qty' => 2,
'items' => [
[
'name' => 'Bar',
'qty' => 2,
'items' => [
[
'name' => 'Baz',
'qty' => 2,
'items' => [],
],
],
],
],
],
];
var_export(expand($items));
Related
I've a four levels of nested array like this:
$array = [
[
'website' => [
'id' => 'one'
],
'children' => [
[
'website' => [
'id' => 'one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.one.two'
],
'children' => []
]
]
],
[
'website' => [
'id' => 'one.one.two'
],
'children' => [
[
'website' => [
'id' => 'one.one.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.two.two'
],
'children' => []
]
]
]
]
],
[
'website' => [
'id' => 'one.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.one'
],
'children' => [
[
'website' => [
'id' => 'one.two.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.one.two'
],
'children' => []
]
]
],
[
'website' => [
'id' => 'one.two.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.two.two'
],
'children' => []
]
]
]
]
]
]
]
];
now, I'd like to remove some of the array element based on some rules. For simplicity, let's say, we'd like to remove array elements that has 'id' equals to one.one.two or one.two.one.
I was looking into some answers provided on stackoverflow and was trying to apply them to solve my issue, but didn't go much. The most chalanging thing is to unset part of an array where it's not the farthest point in the array.
I was trying this to note at which level I want to delete the arrays and then trying to unset them using those array index.
$pointer = [];
foreach($array as $key => $level1) {
$level1pointer = $key;
$pointer[] = markToDelete($level1, $level1pointer);
foreach($level1['children'] as $key => $level2) {
$level2pointer = $key;
$pointer[] = markToDelete($level2, $level1pointer, $level2pointer);
foreach($level2['children'] as $key => $level3) {
$level3pointer = $key;
$pointer[] = markToDelete($level3, $level1pointer, $level2pointer, $level3pointer);
foreach($level3['children'] as $key => $level4) {
$level4pointer = $key;
$pointer[] = markToDelete($level4, $level1pointer, $level2pointer, $level3pointer, $level4pointer);
}
}
}
}
function markToDelete($array, $level1 = null, $level2 = null, $level3 = null, $level4 = null) {
$exclusionList = [
'one.one.two',
'one.two.one'
];
if (!empty($array['website']) && in_array($array['website']['id'], $exclusionList)) {
print_r('marking for deletion: '. $array['website']['id'] . PHP_EOL);
return [
'id' => $array['website']['id'],
'level1' => $level1,
'level2' => $level2,
'level3' => $level3,
'level4' => $level4
];
}
return [];
}
I was also trying to use Iterator like this:
$it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array), \RecursiveIteratorIterator::LEAVES_ONLY);
$newArray = [];
foreach($it as $key => $value) {
$exclusionList = [
'one.one.two',
'one.two.one'
];
if(!in_array($value, $exclusionList)) {
print_r(sprintf('value: %s is ready to be deleted', $value).PHP_EOL);
$newArray[] = $value;
}
}
but I need a way to unset the array when looping through the iterator.
I'd like to get an output like this:
$array = [
[
'website' => [
'id' => 'one'
],
'children' => [
[
'website' => [
'id' => 'one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one'
],
'children' => [
[
'website' => [
'id' => 'one.one.one.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.one.one.two'
],
'children' => []
]
]
],
]
],
[
'website' => [
'id' => 'one.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two'
],
'children' => [
[
'website' => [
'id' => 'one.two.two.one'
],
'children' => []
],
[
'website' => [
'id' => 'one.two.two.two'
],
'children' => []
]
]
]
]
]
]
]
];
I'd really appreciate help on how to resolve this in more efficient way. Thanks.
Here's a recursive function that will do what you want. It traverses the array, looking for children whose website id is in the list of ids to be removed and unsetting them. Note that because of your additional top-level array, you need to iterate the function over those values.
function delete_entries(&$array, $ids_to_delete) {
foreach ($array['children'] as $index => &$child) {
if (in_array($child['website']['id'], $ids_to_delete)) {
unset($array['children'][$index]);
}
delete_entries($child, $ids_to_delete);
}
}
foreach ($array as &$arr) {
delete_entries($arr, array('one.one.two', 'one.two.one'));
}
var_export($array);
The output is as you desire but too long to reproduce here. See the demo on 3v4l.org
Update
The above code won't delete entries on the top level because the array structure is different from lower levels in the array. That can be dealt with in the outer foreach loop:
$excluded = array('two', 'one.one.two', 'one.two.one');
foreach ($array as $key => &$arr) {
if (in_array($arr['website']['id'], $excluded)) {
unset($array[$key]);
}
else {
delete_entries($arr, $excluded);
}
}
Updated demo
I Have this array and i want to get all parent have a balance :
this array is multi rows note that sub-array is not static could be more
Thanks
$array_content = [
'id'=> 4,
'Parent' => [
[
'id' => 54,
'Parent' => [
[
'id' => 324,
'KUI' => 'ABC',
'figure' => 'Tira',
'Parent'=> []
],
[
'id' => 52355,
'lft' => 'LEFT',
'Parent' => [
[
'id' => 4,
'Parent' => [
[
'id' => 234,
'ui' => 'UITed',
'Parent'=> ['Balance'=>450.3]
]
]
],
[
'id' => 76,
'ui' => 'some value',
'Parent'=> []
]
],
]
],
],
[
'id' => 23,
'title' => 'ABC',
'Parent' => [
],
]
]
];
The output that i need to see is this balance by the id =234 from parent:
'Balance'=>450.3
You can also, instead of iterating the array, make it Json and use substr and strpos (or regex) to find the value.
$json = json_encode($array_content);
$pos = strpos($json, "Balance\":")+9; // +9 for Balance": is 9 characters
Echo substr($json, $pos, strpos($json, "}", $pos)-$pos); // 450.3
https://3v4l.org/cojXe
Here i found the answer Thanks everyone :
$array_content = [
'id'=> 4,
'Parent' => [
[
'id' => 54,
'Parent' => [
[
'id' => 324,
'KUI' => 'ABC',
'figure' => 'Tira',
'Parent'=> []
],
[
'id' => 52355,
'lft' => 'LEFT',
'Parent' => [
[
'id' => 4,
'Parent' => [
[
'id' => 234,
'ui' => 'UITed',
'Parent'=> ['Balance'=>450.3]
]
]
],
[
'id' => 76,
'ui' => 'some value',
'Parent'=> []
]
],
]
],
],
[
'id' => 23,
'title' => 'ABC',
'Parent' => [
],
]
]
];
function search($arr,$id)
{
if(gettype($arr) == 'array')
foreach($arr as $key =>$list)
{
if(gettype($list) == 'array'){
if(isset($list['id']))
{
if($list['id'] ==$id)
print_r($list['Parent']);
}
search($list,$id);
}
}
}
foreach($array_content as $key)
{
search($key,234);
}
i have 2 array $dizi1 and $dizi2. And these are like this.
$dizi1 = [
[
'id' => 1,
'name' => 'özkan',
'surname' => 'özdemir',
],
[
'id' => 2,
'name' => 'çağrı',
'surname' => 'uğurel',
],
[
'id' => 3,
'name' => 'can',
'surname' => 'tokay'
],
[
'id' => 4,
'name' => 'lütfü',
'surname' => 'uzun'
]
];
$dizi2 = [
[
'id' => 2,
'birthday' => 1993
],
[
'id' => 3,
'birthday' => 1990
],
[
'id' => 4,
'birthday' => 1989
],
[
'id' => 1,
'birthday' => 1987
]
];
and this is what i want
istenenDizi = [
[
'id' => 1,
'name' => 'özkan',
'surname' => 'özdemir',
'birthday' => 1987,
]
];
i reseaxrh a lot bu i cant find a algortihm to do this. I will also create two excel table and i am gonna use this. Can you please help me how can i do this?
Thanksss!
You can use foreach(), array_search(),array_column() like below:-
$istenenDizi = [];
foreach($dizi1 as $dizi1){
$istenenDizi[$dizi1['id']] = $dizi1;
$istenenDizi[$dizi1['id']]['birthday'] = $dizi2[array_search($dizi1['id'],array_column($dizi2,'id'))]['birthday'];
}
$istenenDizi = array_values($istenenDizi);
print_r($istenenDizi);
Output:-https://eval.in/1000838
As you second array unsorted, you can sort it first. Then combine the two array by items. Demo.
usort($dizi2, function($a, $b){ return $a['id'] > $b['id'];});
foreach($dizi1 as $k=>$v){
$v['birthday'] = $dizi2[$k]['birthday'];
$result[] = $v;
}
I have a multidimensional array that may or may not contain the key name one or more times. What I'd like to do is for every instance of said element insert another element, next to that element, with the key key.
So, given this array:
[
[
'noname' => 'No name',
'label' => 'I have no name'
],
[
'name' => 'foo',
'label' => 'Foo',
'fields' => [
[
'name' => 'bar',
'label' => 'Bar'
]
]
],
[
'name' => 'baz',
'label' => 'Baz'
]
]
I'd like the following output:
[
[
'noname' => 'No name',
'label' => 'I have no name'
],
[
'name' => 'foo',
'key' => 'foo-key', # This is inserted by the function
'label' => 'Foo',
'fields' => [
[
'name' => 'bar',
'key' => 'bar-key', # This is inserted by the function
'label' => 'Bar'
]
]
],
[
'name' => 'baz',
'key' => 'baz-key', # This is inserted by the function
'label' => 'Baz'
]
]
I've looked into array_walk_recursive but can't get it to work. Do I need to write my own recursive function or is there something appropriate built in that I can use for this?
Your code would be something like this:
<?php
$array = [
[
'noname' => 'No name',
'label' => 'I have no name'
],
[
'name' => 'foo',
'label' => 'Foo',
'fields' => [
[
'name' => 'bar',
'label' => 'Bar'
]
]
],
[
'name' => 'baz',
'label' => 'Baz'
]
];
function fix_array($array){
foreach ($array as $key => $value){
if (is_array($value)){
$array[$key] = fix_array($value);
}
elseif ($key == 'name'){
$array['key'] = $value . '-key';
}
}
return $array;
}
$new_array = fix_array($array);
print_r($new_array);
I am trying to make a multi-dimensional array build an array path adding the hr field so it looks like this:
I just can't figure out how to add the totals, nor create a sub-array so the dot notation in an option too. My goal is to get something like this:
[1] => [1][2][1][5][0][6] = 35 (the second child path "1")
[1] => [1][2][1][5][0][7] = 25
or Something like this:
array (
[children.0.children.0.children.0.total] = 20
[children.0.children.1.children.1.total] = 35
// etc
)
The complicated part is that it goes in different directions and I want to know what is the highest and lowest total based on the path:
==> Run Code Here or Copy/Paste
// -------------
// The Flattener
// -------------
function doit($myArray) {
$iter = new RecursiveIteratorIterator(new RecursiveArrayIterator($myArray));
$result = array();
foreach ($iter as $leafKey => $leafValue) {
$keys = array();
foreach (range(0, $iter->getDepth()) as $depth) {
$keys[] = $iter->getSubIterator($depth)->key();
}
$result[ join('.', $keys) ] = $leafValue;
}
return $result;
}
// -------------
// Example Tree
// -------------
$tree = [
'id' => 1,
'type' => 'note',
'data' => [],
'children' => [
[
'id' => 2,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => [
[
'id' => 3,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => [
'id' => 4,
'type' => 'exit',
'data' => [],
'children' => []
]
],
[
'id' => 5,
'type' => 'note',
'data' => [
'hr' => 10,
],
'children' => [
[
'id' => 6,
'type' => 'wait',
'data' => [
'hr' => 10,
],
'children' => []
],
[
'id' => 7,
'type' => 'exit',
'data' => [],
'children' => []
],
]
]
],
]
]
];
$result = doit($tree);
print_r($result);
This seems to work, I found it somewhere googling all day.
array_reduce(array_reverse($keys), function($parent_array, $key) {
return $parent_array ? [$key => $parent_array] : [$key];
}, null);