I'm trying to figure out the best way to DRY a foreach loop. I need the loop to add the node into the tree array only if it has any children.
$categories = Category::all();
$nodes = $categories->toTree();
$tree[] = [
'id' => null,
'name' => '--No Parent--'
];
foreach ( $nodes as $node ) {
$tree[] = [
'id' => $node->id,
'name' => $node->name
];
foreach ( $node->children as $node2 ) {
$tree[] = [
'id' => $node2->id,
'name' => $node->name.' > '. $node2->name
];
}
}
Looks like a simple if that checks for that condition would work for you.
$categories = Category::all();
$nodes = $categories->toTree();
$tree[] = [
'id' => null,
'name' => '--No Parent--'
];
foreach ( $nodes as $node ) {
if ( is_array($node->children) && count($node->children) > 0 ) {
$tree[] = [
'id' => $node->id,
'name' => $node->name
];
foreach ( $node->children as $node2 ) {
$tree[] = [
'id' => $node2->id,
'name' => $node->name.' > '. $node2->name
];
}
}
}
Related
Basically I have an associative array from the database like below,
persons = [
[id=>1, name=>adam, hobbyId=>x, hobbyName=> swim],
[id=>2, name=>brian, hobbyId=>y, hobbyName=> read],
[id=>1, name=>adam, hobbyId=>z, hobbyName=> sing]
]
I want to convert it to a new style like JSON object, something like this,
new_persons = [
[id=>1, name=>adam, hobbies => [[hobbyId=>x, hobbyName=> swim],[hobbyId=>z,
hobbyName=> sing]],
[id=>2, name=>brian, hobbies => [[hobbyId=>y, hobbyName=>read]]
]
What I have tried is,
$new_persons = array();
if (count($persons) > 0) {
foreach ($persons as $item) {
$detail = array(
'id' => $item['id'],
'name' => $item['name'],
'hobbies' => [['hobbyId'=>$item['hobbyId'],'hobbyName'=>$item['hobbyName']]]
);
if (in_array($item['id'], $newArray)) {
array_push($newArray['hobbies'], ['hobbyId'=>$item['hobbyId'],'hobbyName'=>$item['hobbyName']]);
} else {
array_push($newArray, $detail);
}
}
}
But it doesn't meet my requirements, What is wrong with my code?
$t2 = [];
foreach ($t as $item) {
if (!isset($t2[$item['id']])) {
$t2[$item['id']] = [
'id' => $item['id'],
'name' => $item['name'],
];
}
$t2[$item['id']]['hobbies'][] = [
'hobbyId' => $item['hobbyId'],
'hobbyName' => $item['hobbyName'],
];
}
I have array name $main_array
$main_array = [
[
'product_id' => '1',
'values' => '1"'
],
[
'product_id' => '4',
'values' => '1"'
],
[
'product_id' => '4',
'values' => 'blue'
],
[
'product_id' => '5',
'values' => 'blue'
]
];
I want to check values from other array
$check_array = [
'1"','blue'
];
Find product_id where 1" && blue both matching
Expected output ::
$output = [
[
'product_id' => '4',
'values' => '1"'
],
[
'product_id' => '4',
'values' => 'blue'
]
];
You could use an array to store matching elements using the product id as key.
$include = [] ;
foreach ($main_array as $key => $item) {
// if values match to $check_array
if (in_array($item['values'], $check_array)) {
// store using product id as key
$pid = $item['product_id'] ;
$include[$pid][] = $key;
}
}
// Filter to keep only items that match with all conditions
$include = array_filter($include, function($a) use ($check_array) {
return count($a) == count($check_array) ;
}) ;
$include = reset($include) ; // Get the first
// Recreate final array :
$out = [] ;
foreach ($include as $elem) {
$out[] = $main_array[$elem] ;
}
print_r($out);
Will outputs :
Array
(
[0] => Array
(
[product_id] => 4
[values] => 1"
)
[1] => Array
(
[product_id] => 4
[values] => blue
)
)
You may use nested foreach cycles. Supposed that $check_array containes two fields, you can write a code like this:
$output = array();
$match_array = array();
//check the first field correspondence
foreach ($main_array as $key1=>$sub_main) {
foreach ($sub_main as $key2=>$item) {
if ($check_array[0] == $item['values']) {
$match_array[] = $item['product_id'];
}
}
}
//try to match the second field
foreach ($main_array as $key1=>$sub_main) {
foreach ($sub_main as $key2=>$item) {
if ($check_array[1] == $item['values']) {
if (in_array($item['product_id'], $match_array) {
$output[] = array($item['product_id'], $check_array[1]);
$output[] = array($item['product_id'], $check_array[2]);
}
}
}
}
I used ArrayDataProvider to mix models and then merged them into one data-provider. I used the data-provider in grid-view and everything is works fine.
But I need to order the grid-view by one column I tried a lot of solutions but none of them are worked.
This my model code (order by item_order )
public function getAllSelectedItemsInTheUnit($unitId, $grid = false)
{
$finalList = array();
$storiesList = array();
$activityList = array();
$breakList = array();
$stories = UnitStories::find()->joinWith(['story'])->where("unit_id=$unitId")->all();
if (count($stories) > 0) {
foreach ($stories as $item) {
$storiesList[] = [
'key' => self::TYPE_STORY . $item->id,
'id' => $item->id,
'title' => $item->story->title,
'type' => self::TYPE_STORY,
'item_order' => $item->unit_order,
];
}
}
$activities = UnitActivities::find()->joinWith(['activity'])->where("unit_id=$unitId")->all();
if (count($activities) > 0) {
foreach ($activities as $item) {
$activityList[] = [
'key' => self::TYPE_ACTIVITY . $item->id,
'id' => $item->id,
'title' => $item->activity->title,
'type' => self::TYPE_ACTIVITY,
'item_order' => $item->activity_order,
];
}
}
$breaks = UnitBreaks::find()->where("unit_id=$unitId")->all();
if (count($breaks) > 0) {
foreach ($breaks as $item) {
$breakList[] = [
'key' => self::TYPE_BREAK . $item->id,
'id' => $item->id,
'title' => $item->title,
'type' => self::TYPE_BREAK,
'item_order' => $item->unit_order,
];
}
}
$finalList = array_merge($storiesList, $activityList, $breakList);
$dataProvider = new ArrayDataProvider([
'allModels' => $finalList, 'key' => 'key',
'sort' => [
'attributes' => ['item_order'],
],
]);
return $dataProvider;
}
Any solution will be very good even sort array by pure PHP I guess will fix the problem .
You can use usort()
usort($finalList, function ($a, $b) {
return $a['item_order'] < $b['item_order'];
});
Add your condition in callback >, <, <= etc
The question is simple, I want to create the array below dynamically, but the code I got now only outputs the last row. Is there anybody who knows what is wrong with my dynamically array creation?
$workingArray = [];
$workingArray =
[
0 =>
[
'id' => 1,
'name' => 'Name1',
],
1 =>
[
'id' => 2,
'name' => 'Name2',
]
];
echo json_encode($workingArray);
/* My not working array */
$i = 0;
$code = $_POST['code'];
$dynamicArray = [];
foreach ($Optionsclass->get_options() as $key => $value)
{
if ($value['id'] == $code)
{
$dynamicArray =
[
$i =>
[
'id' => $key,
'name' => $value['options']
]
];
$i++;
}
}
echo json_encode($dynamicArray);
You dont need to have the $i stuff that is adding another level to your array that you dont want.
$code = $_POST['code'];
$dynamicArray = [];
foreach ($Optionsclass->get_options() as $key => $value)
{
if ($value['id'] == $code)
{
$dynamicArray[] = ['id' => $key, 'name' => $value['options'];
}
}
echo json_encode($dynamicArray);
You are creating a new dynamic array at each iteration:
$dynamicArray =
[
$i =>
[
'id' => $key,
'name' => $value['options']
]
];
Instead, declare $dynamicArray = []; above the foreach, and then use:
array_push($dynamicArray, [ 'id' => $key, 'name' => $value['options']);
inside the array.
So my example inputs are
$example_1 = Array (
0 => Array (
'category' => 'body',
'sub-category' => 'intro',
'id' => 'header',
'copy' => 'Hello',
),
1 => Array (
'category' => 'body',
'sub-category' => 'intro',
'id' => 'footer',
'copy' => 'Bye',
),
);
$example_2 = Array (
0 => Array (
'category' => 'body',
'sub-category' => 'intro',
'sub-sub-category' => 'header',
'sub-sub-child-category' => 'left',
'id' => 'title',
'copy' => 'Hello',
),
1 => Array (
'category' => 'body',
'sub-category' => 'intro',
'sub-sub-category' => 'footer',
'sub-sub-child-category' => 'right',
'id' => 'title',
'copy' => 'Bye',
),
);
I want to transform it into
$example_output_1 = Array (
'body' => Array (
'intro' => Array (
'header' => Array (
'title' => 'Hello',
),
'footer' => Array (
'title' => 'Bye',
),
),
),
);
$example_output_2 = Array (
'body' => Array (
'intro' => Array (
'header' => Array (
'left' => Array (
'title' => 'Hello',
),
),
'footer' => Array (
'right' => Array (
'title' => 'Bye',
)
),
),
),
);
Note the depth of the array is dynamic (it is not set - only by the time it hits 'copy' does it indicate the depth of the array).
I am having problems trying to get the recursion correctly. The basic but very rough algorithm I had was to
- Loop through the Row
- Loop through the contents of the Row
- When the index is "copy" then the final value is current value.
- Then build the array
I managed to get it to process for ONLY one row of the array but it was very messy and kinda patchy, so I got a feeling I really need to start from scratch.
Updated: Attached embarrassing Code as requested (don't scream! ;p)
function buildArray($row, $start = true) {
if ($start) {
$result = array();
}
if ( ! is_array($row) ) {
return $row;
}
// Get the first element of the array include its index
$cellValue = null;
$colId = null;
foreach($row AS $index => $value) {
$cellValue = $value;
$colId = $index;
break;
}
// Reduce the array by one
$tempRow = $row;
$temp = array_shift($tempRow);
if ($colId == 'copy') {
$result[$cell] = buildArray($cellValue, $locale, false);
} else {
$result[$cell] = buildArray($tempRow, $locale, false);
}
return $result;
}
Any help will be greatly appreciated.
Should be pretty straightforward:
$originalArray = array(); // <-- should contain your values
$newArray = array();
foreach( $originalArray as $item )
{
$category = $item['category'];
$subcategory = $item['sub-category'];
if ( empty( $newArray[$category] ) )
{
$newArray[$category] = array();
}
if ( empty( $newArray[$category][$subcategory] ) )
{
$newArray[$category][$subcategory] = array();
}
$newArray[$category][$subcategory][$item['id']] = $item['copy'];
}
See it here in action: http://codepad.viper-7.com/9bDiLP
Update: Now that you've specified that you need unlimited recursion, here's a shot at that:
$originalArray = array(); // <-- Your values go here
$newArray = array();
foreach ( $originalArray as $item )
{
$inception = &$newArray; // http://www.imdb.com/title/tt1375666/
foreach ( $item as $key => $val )
{
if ( $key != 'id' )
{
if ( empty($inception[$val]) )
{
$inception[$val] = array();
}
$inception = &$inception[$val];
}
else
{
$inception[ $val ] = $item['copy'];
break;
}
}
}
...and here's the demo: http://codepad.viper-7.com/F9hY7h
This can be solved iteratively, because the recursion would only happen at the tail end of your function. The following code is the simplification. It builds a new layered array while it iterates over the old.
After transforming each each entry it gets merged using array_merge_recursive.
function transform($a)
{
// create new array and keep a reference to it
$b = array(); $cur = &$b;
foreach ($a as $key => $value) {
if ('id' === $key) {
// we're done, add value to the array built so far using id and copy
$cur[$value] = $a['copy'];
break;
} else {
// create one more level
$cur[$value] = array();
// and update the reference
$cur = &$cur[$value];
}
}
// all done
return $b;
}
// $example_2 is your multi-dimensional array
$merged = call_user_func_array('array_merge_recursive',
array_map('transform', $example_2)
);