Refactor foreach with multilevel array - php

I'm currently returning results from a sql statement in an array like so:
$results = [];
foreach($promotionTool as $p){
$results[] = $p;
}
return $results;
Which my console shows in an object with this structure:
(2) [{…}, {…}]
0:
codeID: "41"
code: "123ABC"
rule_type: "Category"
attribute_type: "identifier"
attribute_title: "category number"
attribute_value: "234"
1:
codeID: "41"
code: "123ABC"
rule_type: "Category"
attribute_type: "amount"
attribute_title: "percent"
attribute_value: "25"
This is showing the data I expect but I'm a little bit lost on how to restructure this so that I can group on certain levels and finally return only an array of the attributes like so:
codeID
code
rule_type
array(
0:
attribute_type: "identifier"
attribute_title: "category number"
attribute_value: "234"
1:
attribute_type: "amount"
attribute_title: "percent"
attribute_value: "25"
)
How would I refactor my foreach to group at multiple levels in that way?

I guess this is what you are looking for:
<?php
$input = [
[
'codeID' => "41",
'code' => "123ABC",
'rule_type' => "Category",
'attribute_type' => "identifier",
'attribute_title' => "category number",
'attribute_value' => "234"
],
[
'codeID' => "41",
'code' => "123ABC",
'rule_type' => "Category",
'attribute_type' => "amount",
'attribute_title' => "percent",
'attribute_value' => "25"
]
];
$output = [];
array_walk($input, function ($e) use (&$output) {
$output[$e['codeID']][$e['code']][$e['rule_type']][] = [
'attribute_type' => $e['attribute_type'],
'attribute_title' => $e['attribute_title'],
'attribute_value' => $e['attribute_value']
];
});
print_r($output);
This could be a variant easier to read:
array_walk($input, function ($e) use (&$output) {
$codeID = &$e['codeID'];
$code = &$e['code'];
$rule_type = &$e['rule_type'];
$output[$codeID][$code][$rule_type][] = [
'attribute_type' => $e['attribute_type'],
'attribute_title' => $e['attribute_title'],
'attribute_value' => $e['attribute_value']
];
});
The output obviously is:
Array
(
[41] => Array
(
[123ABC] => Array
(
[Category] => Array
(
[0] => Array
(
[attribute_type] => identifier
[attribute_title] => category number
[attribute_value] => 234
)
[1] => Array
(
[attribute_type] => amount
[attribute_title] => percent
[attribute_value] => 25
)
)
)
)
)

Related

How I can explode all string fields in Collection using Laravel

I have an array with fields
[
"house" => "30|30|30",
"street" => "first|second|third",
...
]
I want to get array
[
[
"house" => "30",
"street" => "first",
...
],
[
"house" => "30",
"street" => "second",
...
],
[
"house" => "30",
"street" => "third",
...
]
]
I know how I can solve this using PHP and loop, but maybe this problem has more beautiful solution
use zip
$data = [
"house" => "30|30|30",
"street" => "first|second|third",
];
$house = collect(explode('|',$data['house']));
$street = collect(explode('|',$data['street']));
$out = $house->zip($street);
$out->toarray();
Here's something I managed to do with tinker.
$original = [
"house" => "30|30|30",
"street" => "first|second|third",
];
$new = []; // technically not needed. data_set will instantiate the variable if it doesn't exist.
foreach ($original as $field => $values) {
foreach (explode('|', $values) as $index => $value) {
data_set($new, "$index.$field", $value);
}
}
/* dump($new)
[
[
"house" => "30",
"street" => "first",
],
[
"house" => "30",
"street" => "second",
],
[
"house" => "30",
"street" => "third",
],
]
*/
I tried using collections, but the main problem is the original array's length is not equal to the resulting array's length, so map operations don't really work. I suppose you can still use each though.
$new = []; // Since $new is used inside a Closure, it must be declared.
collect([
"house" => "30|30|30",
"street" => "first|second|third",
...
])
->map(fn($i) => collect(explode('|', $i))
->each(function ($values, $field) use (&$new) {
$values->each(function ($value, $index) use ($field, &$new) {
data_set($new, "$index.$field", $value);
});
});
$array = ["house" => "30|30|30","street" => "first |second| third"];
foreach($array as $key=> $values){
$explodeval = explode('|',$values);
for($i=0; $i<count($explodeval); $i++){
$newarray[$i][$key]= $explodeval[$i];
}
}
output:
Array
(
[0] => Array
(
[house] => 30
[street] => first
)
[1] => Array
(
[house] => 30
[street] => second
)
[2] => Array
(
[house] => 30
[street] => third
)
)

Return result of recursive function

I got a recursive function which currently echo the results. I want this function to return results and loop them with foreach inside markup.
I understand that i should use some kind of iteration for each array to get my desired result but have failed with that. Currently my code look like this(with attempts to iterate):
public static function recursiveChildCategory($categories = array(), $depth = 0, $i = 0) {
$ca = [];
// Loop through categories
foreach($categories as $key => $category){
echo str_repeat(" ", $depth);
echo "<a href='".implode('/', $category['breadcrumb'])."'>{$category['title']}</a>";
echo '<br>';
$ca[$i] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
self::recursiveChildCategory($category['child'], $depth + 1, $i++);
}
}
return $ca;
}
And incoming array to the function:
Array (
[0] => Array (
[id] => 7
[title] => Deserts
[slug] => deserts
[child] => Array (
[0] => Array (
[id] => 8
[title] => Space
[slug] => space
[child] => Array (
[0] => Array (
[id] =>
[title] =>
[slug] =>
[child] => Array ( )
)
)
)
)
)
)
Currently it just returns first level of child categories "Deserts", but nothing about "Space".
As desired result i want function to return all categories with $depth and infinite path to multiple child categoires (to do the same work as currently echo doing).
Thanks in advice
Try this and tell me if it's what you are looking for :
The array for my test :
$array = [
0 => [
"id" => 7,
"title" => "Deserts",
"slug" => "deserts",
"child" => [
0 => [
"id" => 8,
"title" => "Space",
"slug" => "space",
"child" => [
0 => [
"id" => 9,
"title" => "Test",
"slug" => "test"
]
]
]
]
]
];
The recursive function :
function recursiveChildCategory($categories, $depth = 0, $ca = []) {
// Loop through categories
foreach($categories as $key => $category){
$ca[$depth] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
$ca = recursiveChildCategory($category['child'], $depth + 1, $ca);
} else {
break;
}
}
return $ca;
}
Now, how to use it :
$test = recursiveChildCategory($array);
var_dump($test);
And this is the output :
array(3) {
[0]=>
array(2) {
["id"]=>
int(7)
["title"]=>
string(7) "Deserts"
}
[1]=>
array(2) {
["id"]=>
int(8)
["title"]=>
string(5) "Space"
}
[2]=>
array(2) {
["id"]=>
int(9)
["title"]=>
string(4) "Test"
}
}
Here is a link to test it : http://sandbox.onlinephpfunctions.com/
EDIT : I made some modification because in OP example array can have multiple result in first "depth", here is the "new" solution :
The array for test :
$array = [
"0" =>
[ "id" => 3, "title" => "Subcat", "slug" => "subcat", "child" =>
[ "0" =>
[ "id" => 5, "title" => "Subsubcat2", "slug" => "subcat2", "child" =>
[ "0" =>
[ "id" => "", "title" => "", "slug" =>"", "breadcrumb" => [ "0" => "homeworld", "1" => "cat", "2" => "subcat", "3" => "subcat2" ], "child" => [ ] ]
]
]
]
],
"1" =>
[ "id" => 4, "title" => "Kalahari", "slug" => "kalahari", "child" =>
[ "0" => [ "id" => 7, "title" => "deserts", "slug" => "deserts", "child" =>
[ "0" =>
[ "id" => 8, "title" => "Ural", "slug" => "ural", "child" =>
[ "0" => [ "id" =>"", "title" =>"", "slug" =>"", "child" => [ ] ] ]
]
]
]
]
]
];
The function : I just add $ca[$depth][] instead of $ca[$depth]
function recursiveChildCategory($categories, $depth = 0, $ca = []) {
// Loop through categories
foreach($categories as $key => $category){
$ca[$depth][] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
$ca = recursiveChildCategory($category['child'], $depth + 1, $ca);
} else {
break;
}
}
return $ca;
}
And now the result :
$test = recursiveChildCategory($array);
foreach ($test as $depth => $c) {
echo "depth : ".$depth."\n";
foreach ($c as $result) {
echo "Title : ".$result['title']."\n";
}
echo "=============\n";
}
The output is :
depth : 0
Title : Subcat
Title : Kalahari
=============
depth : 1
Title : Subsubcat2
Title : deserts
=============
depth : 2
Title :
Title : Ural
=============
depth : 3
Title :
=============
Test here : link

How to push the condition based value in existing array in PHP

I am having three arrays
topicsSelected
relavantGroups
topicAssingned
$topicsSelected = [ "T-100","T-600"];
$relavantGroups = [
[ "id" => "G-001","name" => "3 A","active" => false ],
["id" => "G-002","name" => "3 B","active" => false]
];
$topicAssingned = [
"G-001" => [
"groupID" => "G-001",
"groupName" => "3 A",
"topics" => [
"T-100" => [
"topicID" => "T-100"
],
"T-200" => [
"topicID" => "T-200"
]
]
],
"G-002" => [
"groupID" => "G-002",
"groupName" => "3 B",
"topics" => [
"T-400" => [
"topicID" => "T-400"
],
"T-500" => [
"topicID" => "T-500"
]
]
],
];
$topicsSelected array values at least one value should present $topicAssingned means based on groupID, i have to push one value to $relavantGroups like disable : D suppose value not present means disable : A
Expected output:
[
"id" => "G-001",
"name" => "3 A",
"active" => false,
"disable" => "D"
],
[
"id" => "G-002",
"name" => "3 B",
"active" => false,
"disable" => "A"
]
<?php
$topicsSelected = [ "T-100","T-600"];
$relavantGroups = [
[ "id" => "G-001","name" => "3 A","active" => false ],
["id" => "G-002","name" => "3 B","active" => false]
];
$topicAssigned = [
"G-001" => [
"groupID" => "G-001",
"groupName" => "3 A",
"topics" => [
"T-100" => [
"topicID" => "T-100"
],
"T-200" => [
"topicID" => "T-200"
]
]
],
"G-002" => [
"groupID" => "G-002",
"groupName" => "3 B",
"topics" => [
"T-400" => [
"topicID" => "T-400"
],
"T-500" => [
"topicID" => "T-500"
]
]
],
];
$topic_selected_map = [];
foreach($topicsSelected as $each_topic){
$topic_selected_map[$each_topic] = true;
}
$relevant_group_map = [];
foreach($relavantGroups as $each_group){
$relevant_group_map[$each_group['id']] = $each_group;
}
$result = [];
foreach($topicAssigned as $each_assigned_topic){
if(!isset($relevant_group_map[$each_assigned_topic['groupID']])) continue;
$topics_not_found = true;
foreach($each_assigned_topic['topics'] as $each_topic => $topic_details){
if(isset($topic_selected_map[$each_topic])){
$topics_not_found = false;
break;
}
}
$result[] = [
'id' => $each_assigned_topic['groupID'],
'name' => $each_assigned_topic['groupName'],
'active' => $relevant_group_map[$each_assigned_topic['groupID']]['active'],
'disable' => ($topics_not_found === true ? 'A' : 'D')
];
}
print_r($result);
Output:
Array
(
[0] => Array
(
[id] => G-001
[name] => 3 A
[active] => false
[disable] => D
)
[1] => Array
(
[id] => G-002
[name] => 3 B
[active] => false
[disable] => A
)
)
First, make a map(associative array) of values of $topicsSelected. Same goes for $relavantGroups. This is to make the check more efficient. See more on Hash Table.
Now, iterate over $topicAssigned and then iterate over each group's topics inside it. Now, check if a topic exists inside $topicsSelected using a simple isset function. If yes, we disable them, else we don't.
It's not very clear what you are asking and the code is a bit weird but I'll give it a try.
First fix your array declaration - you should use => and not :;
You have to Iterate over the $relavantGroups and for each element iterate the $topicAssingned array. Then perform a simple comparison to see if the group Id is present and you are done!
Here is my solution (quick and dirty): You can see it here
foreach ($relavantGroups as &$g) {
$found = false;
foreach ($topicAssingned as $key => $assigned) {
if ($key === $g["id"] && is_array($assigned["topics"])) {
foreach ($assigned["topics"] as $topic) {
if (in_array($topic["topicID"], $topicsSelected)) {
$found = true;
break;
}
}
}
}
$g["disable"] = $found ? "D" : "A";
}
var_dump($relavantGroups);
Updated the solution - note that I'm using in_array() to determine if the topicID is present. That mean that any value that is in the $topicsSelected array will affect the result.
Hope I helped.
This will output (based one your example):
array(2) {
[0]=> array(4) {
["id"]=> string(5) "G-001"
["name"]=> string(3) "3 A"
["active"]=> bool(false)
["disable"]=> string(1) "D"
}
[1]=> array(4) {
["id"]=> string(5) "G-002"
["name"]=> string(3) "3 B"
["active"]=> bool(false)
["disable"]=> string(1) "A"
}
}

Use array_filter on multi dimensional array

I'm trying to filter an array that looks like this
$array = array(
"id" => "SomeID",
"name" => "SomeName",
"Members" => array(
"otherID" => "theValueIamLookingFor",
"someOtherKey" => "something"
)
);
Now, I'm filtering for data sets, where "otherID" is a certain value. I know I could use array_filter to filter for "id", but I can't for the life of me figure out how to filter for a value in an array inside an array.
Adding some of the data as provided by the WebAPI (I run that through json_decode to create an associative array before all this filtering business)
[
{
"id": "b679d716-7cfa-42c4-9394-3abcdged",
"name": "someName",
"actualCloseDate": "9999-12-31T00:00:00+01:00",
"members": [
{
"otherID": "31f27f9e-abcd-1234-aslkdhkj2j4",
"name": "someCompany"
}
],
"competitor": null,
},
{
"id": "c315471f-45678-4as45-457-asli74hjkl",
"name": "someName",
"actualCloseDate": "9999-12-31T00:00:00+01:00",
"members": [
{
"otherID": "askgfas-agskf-as",
"name": "someName"
}
],
"competitor": null,
},
]
You can do something like:
$arr = array(
array(
"id" => "SomeID",
"name" => "SomeName",
"Members" => array (
"otherID" => "ThisIsNottheValueIamLookingFor",
"someOtherKey" => "something"
)
),
array(
"id" => "SomeID",
"name" => "SomeName",
"Members" => array (
"otherID" => "theValueIamLookingFor",
"someOtherKey" => "something"
)
),
array(
"id" => "SomeID",
"name" => "SomeName",
"Members" => array (
"otherID" => "ThisIsNottheValueIamLookingForEither",
"someOtherKey" => "something"
)
),
);
$result = array_filter($arr, function( $v ){
return $v["Members"]["otherID"] == "theValueIamLookingFor";
});
This will result to:
Array
(
[1] => Array
(
[id] => SomeID
[name] => SomeName
[Members] => Array
(
[otherID] => theValueIamLookingFor
[someOtherKey] => something
)
)
)
Here is the doc for more info: http://php.net/manual/en/function.array-filter.php
UPDATE
On you Updated array, the structure of array is different. You have to use $v["members"][0]["otherID"] to get the otherID
Please try the code below:
$result = array_filter($arr, function( $v ){
return $v["members"][0]["otherID"] == "theValueIamLookingFor";
});

Php find key for min value in 2D array

I have the following 2D array and I would like to get the key of the smalest value in the [0] column if done is equal to no
$graph= array(
"CityA" => array(
"0" => "1",
"1" => "CityC",
"done" => "no",
),
"CityB" => array(
"0" => "4",
"1" => "CityA",
"done" => "no",
),
"CityC" => array(
"0" => "5",
"1" => "CityA",
"done" => "no",
),
);
Try this,
$arr = array_map(function($v){return $v[0];}, $graph);
$key = array_keys($arr, min($arr));
Here you go.
$tes = min( array_column( $graph, 0 ) );
$key = array_search( $tes, array_column( $graph, 0 ) );
$array_keys = array_keys($graph);
echo $array_keys[$key];
You should perform all of your checks in a single pass through your array.
My snippet will provide the first qualifying (contains the lowest [0] value AND has a done value of no) row's key.
Code: (Demo)
$graph = [
"CityB" => ["0" => "1", "1" => "CityA", "done" => "no"],
"CityA" => ["0" => "1", "1" => "CityC", "done" => "no"],
"CityD" => ["0" => "1", "1" => "CityD", "done" => "yes"],
"CityC" => ["0" => "5", "1" => "CityA", "done" => "no"]
];
$result = [];
foreach ($graph as $key => $row) {
if ($row['done'] === 'no' && (!isset($result[$key]) || $row[0] < $result[$key])) {
$result[$key] = $row[0];
}
}
echo key($result) ?? 'No "done => no" rows';
Output:
CityB

Categories