find and loop items in array by id - php

I have an array what contains multiple indexes like
$arr = array(
1 => array('id' => 0,
'container' => 1
),
2 => array('id' => 1,
'container' => 1
),
3 => array('id' => 2,
'container' => 2
),
4 => array('id' => 3,
'container' => 1
)
);
How can I find and loop through all items where container is 1 and after that loop all items where container is 2 and so on..

Besides from using the wrong array assignment :. Just change it to =>.
Then just use a foreach loop:
$containers = array(1, 2);
foreach($containers as $container) {
foreach ($arr as $value) {
if($container == $value['container']) {
echo "Container: $container : ";
echo $value['id'] . '<br/>';
}
}
}

There are many ways to do this. This one re-structures/groups the array by a given key - which is feasible if you iterate over all elements anyway.
<?php
$arr = array(
1 => array('id' => 0,
'container' => 1
),
2 => array('id' => 1,
'container' => 1
),
3 => array('id' => 2,
'container' => 2
),
4 => array('id' => 3,
'container' => 1
)
);
$byContainer = groupBy($arr, 'container');
foreach($byContainer as $container=>$items) {
echo $container, "\r\n";
foreach($items as $e) {
echo ' ', $e['id'], "\r\n";
}
}
function groupBy($arr, $key) {
$result = array();
foreach($arr as $e) {
if ( !isset($result[ $e[$key] ]) ) {
$result[ $e[$key] ] = array($e);
}
else {
$result[ $e[$key] ][] = $e;
}
}
return $result;
}

<?php
$arr = array(1 => array('id'=> 0, 'container'=> 1),
2 => array('id'=> 1, 'container'=> 1),
3 => array('id'=> 2, 'container'=> 2),
4 => array('id'=> 3, 'container'=> 1));
$temp = array();
foreach ($arr as $elem) {
foreach ($elem as $k => $v) {
if ($k == 'container') {
$temp[$k][$v][] = $v;
}
}
}
print_r($temp);
?>
Working Example

Related

Filter array with array_walk_recursive but deny specific values

I am trying to filter an array
$array = [
[
'id' => 1,
'some_key' => 'some_value',
'attribute' => [
'id' => 45,
'cat_id' => 1
],
'sub' => [
'id' => 17,
'some_key' => 'some_value',
'attribute' => [
'id' => 47,
'cat_id' => 17
],
],
],
[
'id' => 2,
'some_key' => 'some_value',
'sub' => [
'id' => 19,
'some_key' => 'some_value',
],
]
];
$childArray = [];
array_walk_recursive($array, static function($value, $key) use(&$childArray){
if($key === 'id') {
$childArray[] = $value;
}
});
This returns me an array of all array-fields having id as key.
[1,45,17,47,2,19]
But there is a small problem, some of the array have an key called attribute containing an idkey field that I dont want to have.
[1,17,2,19] //this is what I want
Is there a way to say "don't take the id inside attribute" ?
My current solution, I added a filter before my filter :D
/**
* #param array $array
* #param string $unwanted_key
*/
private function recursive_unset(&$array, $unwanted_key)
{
unset($array[$unwanted_key]);
foreach ($array as &$value) {
if (is_array($value)) {
$this->recursive_unset($value, $unwanted_key);
}
}
}
but this seems like this is not the best practice ^^
You can traverse recursively manually instead of array_walk_recursive and avoid all under attribute key.
<?php
$childArray = [];
function collectIDs($arr,&$childArray){
foreach($arr as $key => $value){
if($key === 'attribute') continue;
if(is_array($value)) collectIDs($value,$childArray);
else if($key === 'id') $childArray[] = $value;
}
}
collectIDs($array,$childArray);
print_r($childArray);
Demo: https://3v4l.org/V6uFf
Find a function that will flatten your array. The result should look like this (I have a class for this):
array (
0 =>
array (
'id' => 1,
'some_key' => "some_value",
'attribute.id' => 45,
'attribute.cat_id' => 1,
'sub.id' => 17,
'sub.some_key' => "some_value",
'sub.attribute.id' => 47,
'sub.attribute.cat_id' => 17,
),
1 =>
array (
'id' => 2,
'some_key' => "some_value",
'sub.id' => 19,
'sub.some_key' => "some_value",
),
)
So you have all keys available and can work with a modified array_walk.
$childArray = [];
array_walk_recursive($data, static function($value, $key) use(&$childArray){
$keys = array_reverse(explode(".",$key));
if($keys[0] === 'id' AND (!isset($keys[1]) OR $keys[1] != 'attribute')) {
$childArray[] = $value;
}
});
The RecursiveArrayIterator class is also very suitable when array values ​​are to be collected depending on keys and values ​​on different levels.
$result = [];
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($it as $key => $value) {
$parentLevel = $it->getDepth()-1;
$parentKey = $it->getSubIterator($parentLevel)->key();
if($key === 'id' AND $parentKey !== 'attribute'){
$result[] = $value;
}
}
var_export($result);
//array ( 0 => 1, 1 => 17, 2 => 2, 3 => 19, )
To return the qualifying ids instead of creating a reference variable, merge recursive calls of the function as you iterate.
Code: (Demo)
function getIds($array) {
$ids = [];
foreach ($array as $key => $value) {
if ($key === 'id') {
$ids[] = $value;
} elseif ($key !== 'attribute' && is_array($value)) {
$ids = array_merge($ids, getIds($value));
}
}
return $ids;
}
var_export(getIds($array));
Output:
array (
0 => 1,
1 => 17,
2 => 2,
3 => 19,
)

Recursive function to build an array

Can anyone see why this isn't working?
private static function traverseTree($tree,$node)
{
$nodes = array();
$results = array();
$children = $tree->where('parent_id',$node->id)->where('type',UserGroup::DEPARTMENT_TYPE);
foreach($children as $child){
$nodes[] = $child->id;
$children = self::traverseTree($tree,$child);
$results = array_merge ($nodes,$children);
}
return $results;
}
I am stepping through it and I can see that the children are getting found but they are not included in the final results.
The final results just contain the top level.
Detail added:
$tree is a Laravel Collection, if I pipe this to an array I get:
array (
0 =>
array (
'id' => 21,
'name' => 'Top',
'type' => 1,
'parent_id' => 0,
),
1 =>
array (
'id' => 33,
'name' => 'UKDept',
'type' => 2,
'parent_id' => 21,
),
2 =>
array (
'id' => 36,
'name' => 'UKSubDept',
'parent_id' => 33,
),
3 =>
array (
'id' => 37,
'name' => 'USDept',
'type' => 2,
'parent_id' => 21,
),
)
$node:
array (
'id' => 21,
'name' => 'Top',
'type' => 1,
'parent_id' => 0,
),
The first call to populate $children gives:
array (
0 =>
array (
'id' => 33,
'name' => 'UKDept',
'type' => 2,
'parent_id' => 21,
),
1 =>
array (
'id' => 37,
'name' => 'USDept',
'type' => 2,
'parent_id' => 21,
),
)
Try taking the array outside the function.
$nodes = array();
$results = array();
private static function traverseTree($tree,$node)
{
$children = $tree->where('parent_id',$node->id)->where('type',UserGroup::DEPARTMENT_TYPE);
foreach($children as $child){
$nodes[] = $child->id;
$children = self::traverseTree($tree,$child);
$results = array_merge ($nodes,$children);
}
return $results;
}
It will not reinitialize them each time you call the function.Check if it works?
Looks like $child is an array but you are treating it like an object. Try changing:
$nodes[] = $child->id;
to
$nodes[] = $child['id'];
I have this working now, one stupid mistake I made was:
foreach($children as $child){
$nodes[] = $child->id;
$children = self::traverseTree($tree,$child);
$results = array_merge ($nodes,$children);
Using $children as a variable name twice!
This is the complete function:
private static function traverseTree($tree,$node)
{
$initialId = array();
$results = array();
$results[] = $node->id;
$children = $tree->where('parent_id',$node->id);
foreach($children as $node){
//if this is a department, then we need to go deeper
if($node->type == DEPARTMENT) {
$nodesChildren = self::traverseTree($tree, $node);
$results = array_merge ($results,$nodesChildren);
}else{
//just add this node to the results array
$results[] = $node->id;
}
}
return $results;
}

Create a tree array PHP

I have an array:
$data_array = array(
array('id'=> 1 , 'parent_id' => 1 , 'name'=>'Air Vehicles' ),
array('id'=> 2 , 'parent_id' => 2 , 'name'=>'Land Vehicles' ),
array('id'=> 3 , 'parent_id' => 3 , 'name'=>'Water Vehicles' ),
array('id'=> 4 , 'parent_id' => 2 , 'name'=>'Bikes' ),
array('id'=> 5 , 'parent_id' => 2 , 'name'=>'Cars' ),
array('id'=> 6 , 'parent_id' => 1 , 'name'=>'Aero Planes'),
array('id'=> 7 , 'parent_id' => 1 , 'name'=>'Helicopter'),
array('id'=> 8 , 'parent_id' => 3 , 'name'=>'Ships'),
array('id'=> 9 , 'parent_id' => 1 , 'name'=>'Hoverboard'),
array('id'=> 10 , 'parent_id' => 2 , 'name'=>'Hoverboard'),
array('id'=> 11 , 'parent_id' => 4 , 'name'=>'R1 Kawasaki'),
array('id'=> 12 , 'parent_id' => 4 , 'name'=>'Suzuki Hayabusa'),
);
I want to create a tree array from above array:
Air Vechiles
-Aero Planes
-Helicopter
-HoverBoard
Land Vechiles
-Bikes
--R1 Kawasaki
--Suzuki Hayabusa
-Cars
Water Vehicles
-Ships
Here is your desired solution.
class CategoryTree{
public $padding_array;
public function __construct(){
$this->padding_array = array();
}
public function getTreeArray($data_array){
foreach($data_array as $i=>$data){
if($data['id'] == $data['parent']) $data_array[$i]['parent'] = 0;
}
return $this->getCategoryTreeArray($data_array);
}
public function getCategoryTreeArray($elements, $parent_id = 0){
$branch = array();
foreach ($elements as $element) {
if ($element['parent'] == $parent_id) {
$children = $this->getCategoryTreeArray($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[$element['id']] = $element;
}
}
return $branch;
}
public function getCatgoryPadingArray($tree_array, array &$padding_array, $depth=0){
foreach ($tree_array as $id => $data_array) {
$padding_array[$id] = str_repeat('-', $depth).$data_array['name'];
if(is_array($data_array) && array_key_exists('children', $data_array)){
$this->getCatgoryPadingArray($data_array['children'], $padding_array , $depth+1);
}
}
}}
Using class:
$data_array = array(); // your array data
$tree_object = new CategoryTree();
$category_tree_array = $tree_object->getTreeArray($data_array);
$tree_object->getCatgoryPadingArray($category_tree_array, $tree_object->padding_array);
$category_padding_array = $tree_object->padding_array;
echo('<pre>');
print_r($category_padding_array);
echo('<pre>');
You can do it with recursive calling of custom function or like this:
$data_array = array(
array('id' => 1, 'parent_id' => 1, 'name' => 'Air Vehicles'),
array('id' => 2, 'parent_id' => 2, 'name' => 'Land Vehicles'),
array('id' => 3, 'parent_id' => 3, 'name' => 'Water Vehicles'),
array('id' => 4, 'parent_id' => 2, 'name' => 'Bikes'),
array('id' => 5, 'parent_id' => 2, 'name' => 'Cars'),
array('id' => 6, 'parent_id' => 1, 'name' => 'Aero Planes'),
array('id' => 7, 'parent_id' => 1, 'name' => 'Helicopter'),
array('id' => 8, 'parent_id' => 3, 'name' => 'Ships'),
array('id' => 9, 'parent_id' => 1, 'name' => 'Hoverboard'),
array('id' => 10, 'parent_id' => 2, 'name' => 'Hoverboard'),
array('id' => 11, 'parent_id' => 4, 'name' => 'R1 Kawasaki'),
array('id' => 12, 'parent_id' => 4, 'name' => 'Suzuki Hayabusa'),
);
$tree = array();
foreach ($data_array as $k => &$val) {
if ($val['parent_id'] == $val['id']) {
if (empty($tree[$val['parent_id']])) {
$tree[$val['parent_id']] = array();
}
$tree[$val['parent_id']] = array_merge($tree[$val['parent_id']], $val);
} else {
$tree[$val['parent_id']]['sub'][] = $val;
}
}
print_r($tree);
For creating a tree array use a Recursive function. This is working perfectly for me :-
foreach($data_array as $i=>$data)
{
if($data['id'] == $data['parent_id']) $data_array[$i]['parent_id'] = 0;
}
function buildTree(array $elements, $parentId = 0)
{
$branch = array();
foreach ($elements as $element)
{
if ($element['parent_id'] == $parentId)
{
$children = buildTree($elements, $element['id']);
if ($children) $element['children'] = $children;
$branch[] = $element;
}
}
return $branch;
}
$tree = buildTree($data_array);
echo '<pre>', print_r($tree, TRUE);
Try this
$new = array();
foreach ($data_array as $a){
$new[$data_array['parentid']][] = $a;
}
$tree = createTree($new, array($data_array[0]));
print_r($tree);
function createTree(&$list, $parent){
$tree = array();
foreach ($parent as $k=>$l){
if(isset($list[$l['id']])){
$l['children'] = createTree($list, $list[$l['id']]);
}
$tree[] = $l;
}
return $tree;
}
If you look at this example, you should be able to figure out that all you need to do is replace the array name and values to accommodate your requirements. Clearly your data is not numbers so you need to find out for yourself how you would deal with the vehicle names.
$tmpArray = array(array("one",array(1,2,3)),array("two",array(4,5,6)),array("three",array(7,8,9)));
And use print to check the structure
print_r($tmpArray);

PHP: merge multidimensional, associative arrays (LEFT JOIN simulation - keep duplicates, keep non-existent elements)

I got two associative, multidimensional arrays $arrayOffered and $arraySold. I would like to merge them under certain conditions:
if value of key 'item' from $arrayOffered exists in $arraySold, both elements should be included in array $result. If for 1 element from $arrayOffered there are 3 elements in $arraySold, I should get also 3 elements in $result.
otherwise, element from $arrayOffered should be added into $result.
One element from $arrayOffered can have >1 equivalents in $arraySold. They should be joined in the way shown below.
Input data:
$arrayOffered = array(
0 => array('item' => 'product_1', 'Category' => 'ABC'),
1 => array('item' => 'product_2', 'Category' => 'DEF')
);
$arraySold = array(
0 => array('item' => 'product_1', 'ItemsSold' => '2', 'ItemsReturned' => 1), //arrays in this array can contain up to 30 elements
1 => array('item' => 'product_1', 'ItemsSold' => '1')
);
Desired result:
$desiredResult = array(
0 => array('item' => 'product_1', 'Category' => 'ABC', 'ItemsSold' => '2', 'ItemsReturned' => 1),
1 => array('item' => 'product_1', 'Category' => 'ABC', 'ItemsSold' => '1'),
2 => array('item' => 'product_2', 'Category' => 'DEF')
);
I got stuck on something like:
$result = array();
foreach ($arrayOffered as $keyOffered => $offeredSubArr)
{
$item = $offeredSubArr['item'];
foreach($arraySold as $keySold => $soldSubArr)
{
if(isset($soldSubArr['item']) && $soldSubArr['item'] == $item)
{
$i = 0;
$test = array_merge($offeredSubArr, $soldSubArr);
$result[$i][] = $test;
$i++;
}
else
{
$result[$i][] = $offeredSubArr;
$i++;
}
}
}
Problem:
- output array isn't formatted the way I wanted
- I know I'm not going in the right direction. Can you please give me a hint?
This is an option, since you have this $arrayOffered as a kind of master file I suggest to build a hash with this array and use later on the foreach look for sold array.
$arrayOffered = array(
0 => array('item' => 'product_1', 'Category' => 'ABC'),
1 => array('item' => 'product_2', 'Category' => 'DEF')
);
$arraySold = array(
0 => array('item' => 'product_1', 'ItemsSold' => '2', 'ItemsReturned' => 1), //arrays in this array can contain up to 30 elements
1 => array('item' => 'product_1', 'ItemsSold' => '1')
);
//Build a hash to get the extra properties
$hashArray = array();
foreach ($arrayOffered as $offered) {
$hashArray[$offered['item']]=$offered;
}
$resultArray = array();
foreach ($arraySold as $sold) {
$hashItem = $hashArray[$sold['item']];
// you dont want this sold flag on your final result
unset($hashItem['sold']);
$resultArray[]=array_merge($hashItem,$sold);
$hashArray[$sold['item']]['sold']= true;
}
//Add all the missing hash items
foreach($hashArray as $hashItem){
if(!isset($hashItem['sold'])){
$resultArray[]=$hashItem;
}
}
print_r($resultArray);
Test sample
http://sandbox.onlinephpfunctions.com/code/f48ceb3deb328088209fbaef4f01d8d4430478db
$result = array();
foreach ($arrayOffered as $keyOffered => $offeredSubArr)
{
$item = $offeredSubArr['item'];
foreach($arraySold as $keySold => $soldSubArr)
{ $i = 0;
if(isset($soldSubArr['item']) && $soldSubArr['item'] == $item)
{
$test = array_merge($offeredSubArr, $soldSubArr);
$result[$i][] = $test;
}
else
{
$result[$i][] = $offeredSubArr;
}
$i++;
}
}
$result = $result[0];
echo '<pre>'; print_r($result); die();
Well i will try to follow your logic although there is simpler solutions.
First of all we will need to search in a multidimentional array thats why we will need the followed function from this so thread
function in_array_r($needle, $haystack, $strict = false) {
foreach ($haystack as $item) {
if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && in_array_r($needle, $item, $strict))) {
return true;
}
}
return false;
}
Next after small changes:
$i you don't need to make it zero on every loop just once so place it outside
unnecessary [] ($result[$i][]) you don't need the empty brackets no reason to create an extra table in the $i row since what you add there, the $test is already table itself
Adding the last loop coz when sth is not in the second table it will be added in your new table in every loop and as far as i get you don't want that kind of duplicates
We have the following code:
$arrayOffered = array(
0 => array('item' => 'product_1', 'Category' => 'ABC'),
1 => array('item' => 'product_2', 'Category' => 'DEF')
);
$arraySold = array(
0 => array('item' => 'product_1', 'ItemsSold' => '2', 'ItemsReturned' => 1), //arrays in this array can contain up to 30 elements
1 => array('item' => 'product_1', 'ItemsSold' => '1')
);
$i = 0;
$result = array();
foreach ($arrayOffered as $keyOffered => $offeredSubArr)
{
$item = $offeredSubArr['item'];
foreach($arraySold as $keySold => $soldSubArr)
{
if(isset($soldSubArr['item']) && $soldSubArr['item'] == $item)
{
$test = array_merge($offeredSubArr, $soldSubArr);
$result[$i] = $test;
$i++;
}
}
}
foreach ($arrayOffered as $value)
{
if (!in_array_r($value['item'], $result))
{
$result[$i] = $value;
$i++;
}
}
print_r($result);
Which as far as i tested gives the wanted result.

Merge two multidimensional arrays but preserve only associative keys

I would like to merge two arrays recursively, leaving associative keys in place, but replace other. I tried using is_numeric() for key checking, but keys '10' and '11' are replaced.
Sample arrays:
Array1:
$arr1['assoc']='foobar';
$arr1[]='test index';
$arr1[][]=array(3,4);
$arr1['10']['test10'][]=array(0,1);
$arr1['11']['test11']=45;
$arr1['multiarray'][]=array(0,1);
Array2:
$arr2[]=1;
$arr2[][]=2;
$arr2['assoc']='test passed';
$arr2['10']['test10'][]=array(0,2);
$arr2['11']['test11']=array(4,5);
$arr2['multiarray'][]=array(0,2);
How to merge them to get this (with a general function):
array(
'assoc' => 'test passed',
0 => 'test index',
1 => array(
0 => array(
0 => 3,
1 => 4,
),
),
10 => array(
'test10' => array (
0 => array(
0 => 0,
1 => 1,
),
1 => array(
0 => 0,
1 => 2,
),
),
),
11 => array(
'test11' => array (
1 => 4,
2 => 5,
),
),
'multiarray' => array(
0 => array(
0 => 0,
1 => 1,
),
1 => array(
0 => 0,
0 => 2,
),
),
2 => 1,
3 => array(
0 => 2,
),
)
Preserving keys' order is not important.
[Edit] Solution:
function array_max_index_key($arr) {
$prev = -1;
if(is_array($arr)) {
$keys = array_keys($arr);
foreach($keys as $k => $key) {
if(is_numeric($key)) {
if($key == $prev+1) {
$prev=$key;
} else break;
}
}
}
return $prev;
}
function justfortesting($a1, $a2) {
$res=$a1;
$max_key=array_max_index_key($res);
if(is_array($a1)) {
foreach ($a2 as $k => $v) {
if(is_numeric($k) && $k<=$max_key) {
$max_key++;
$res[$max_key]=$v;
} elseif (is_array($v)) {
$res[$k]=justfortesting($a1[$k], $v);
} else {
$res[$k]=$v;
}
}
} else {
$res=$a2;
}
return $res;
}
$arr3=justfortesting($arr1, $arr2);
Check for is_string() instead of is_numeric()
Edited:
when you ask for
is_numeric($key)
check for
is_numeric($key) and !is_string($key)

Categories