I have an array in an array in an array. How can I search if any of these arrays have a specific key and value? If not remove this array from array.
Example:
array:3 [▼
0 => array:2 [▼
"location" => array:4 [▶]
"promotions" => array:1 [▶]
]
1 => array:2 [▼
"city" => array:4 [▶]
"promotions" => array:2 [▼
0 => array:5 [▶]
1 => array:5 [▼
"distance" => "0.2288511878121104"
"promoid" => 54
"promo" => Promotion {#1259 ▶}
"product" => Products {#1162 ▶}
"productID" => 5
]
]
]
2 => array:2 [▼
"city" => array:4 [▶]
"promotions" => []
]
]
I want to search "productID" with value 5, and I want to remove array in promotions which doesn`t have this value.
SOLVED
foreach ($locations as $key => $location) {
foreach ($location['promotions'] as $item => $promotions) {
if (is_array($promotions)) {
foreach ($promotions as $k => $value) {
if (!is_array($value)) {
if ($k == 'productID' && $value != $id) {
unset($locations[$key]['promotions'][$item]);
}
}
}
}
}
}
You could use a recursive function and unset() the target
<?php
// example code
$a = [
'test' => 'foo',
'bar' => [
'productID' => 5,
'something' => 'else'
],
'bar2' => [
'productID' => 6,
'something2' => 'else'
]
];
function removeItem($array, $search) {
$s = explode(":",$search);
$skey = trim($s[0]);
$sval = trim($s[1]);
foreach ($array as $n => $v) {
if ($n == $skey && $v == $sval) {
unset($array[$n]);
} else {
if (is_array($v)) $v = removeItem($v, $search);
$array[$n] = $v;
}
}
return $array;
}
$a = removeItem($a, 'productID:5');
print_r($a);
example: https://www.tehplayground.com/zJ2bKplP1pDaV8Ss
Nice solve, you can skip the 3er loop, checking if the key is set with isset()
foreach ($arr as $key => $location) {
if (!is_array($location)) { //if child is not an array ignore it
continue;
}
foreach ($location as $item => $promotions) {
if (!is_array($location)) {//if child is not an array ignore it
continue;
}
if (
isset($promotions['productID']) && //3er lvl, Has the key?
$promotions['productID'] == $id //Has the id
) {
continue; //This is a match so, ignore it
} else {
unset($arr[$key][$item]); //This promotion doesn't have it.
}
}
}
And this one checks at all levels, it uses recursive fn
$ans = deepClean($arr, 'productID', 5);
function deepClean($arr, $search_key, $search_value): ?array
{
if (
isset($arr[$search_key]) && //Has the key
$arr[$search_key] == $search_value //Match value
) {
return $arr;
}
//check children
$currentLevel = [];
foreach ($arr as $key => $value) {
if (!is_array($value)) {
continue;
}
$deepArr = deepClean($value, $search_key, $search_value);
if (is_array($deepArr)) { //child has search?
$currentLevel[$key] = $deepArr;
}
}
return $currentLevel === [] ? null : $currentLevel;
}
Related
I have a problem that has been stressing me out for weeks now and i cannot find a clean solution to it that does not involve recursion.
This is the problem:
Take a flat array of nested associative arrays and group this into one deeply nested object. The top level of this object will have its parent property as null.
This is my solution but i admit it is far from perfect. I am fairly certain this can be done in a single loop without any recursion, but for the life of me i cannot work it out!
//Example single fork
$data = array(
//Top of Tree
0 => array(
"name" => "A",
"parent" => null,
"id" => 1,
),
//B Branch
1 => array(
"name" => "B",
"parent" => "1",
"id" => 2,
),
2 => array(
"name" => "B1",
"parent" => "2",
"id" => 3,
),
3 => array(
"name" => "B2",
"parent" => "3",
"id" => 4,
),
4 => array(
"name" => "B3",
"parent" => "4",
"id" => 5,
),
//C Branch
5 => array(
"name" => "C",
"parent" => "1",
"id" => 6,
),
6 => array(
"name" => "C1",
"parent" => "6",
"id" => 7,
),
7 => array(
"name" => "C2",
"parent" => "7",
"id" => 8,
),
8 => array(
"name" => "C3",
"parent" => "8",
"id" => 9,
),
);
Actual anonymised example
array:7214 [▼
0 => array:3 [▼
"name" => ""
"parent" => null
"id" =>
]
1 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
2 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
3 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
4 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
5 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
6 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
7 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
8 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
9 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
10 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
Another example deeper nesting
{
"name":"top",
"id":xxx,
"children":{
"second":{
"name":"second",
"id":xxx,
"children":{
"Third":{
"name":"third",
"id":xxx,
"children":{
"fourth":{
"name":"fourth",
"id":xxx
}
}
}
}
}
}
}
$originalLength = count($data);
$obj = [];
while ($originalLength > 0) {
foreach ($data as $item) {
$name = $item['name'];
$parent = $item['parent'];
$a = isset($obj[$name]) ? $obj[$name] : array('name' => $name, 'id'=>$item['id']);
if (($parent)) {
$path = get_nested_path($parent, $obj, array(['']));
try {
insertItem($obj, $path, $a);
} catch (Exception $e) {
continue;
//echo 'Caught exception: ', $e->getMessage(), "\n";
}
}
$obj[$name] = isset($obj[$name]) ? $obj[$name] : $a;
$originalLength--;
}
}
echo json_encode($obj['A']);
function get_nested_path($parent, $array, $id_path)
{
if (is_array($array) && count($array) > 0) {
foreach ($array as $key => $value) {
$temp_path = $id_path;
array_push($temp_path, $key);
if ($key == "id" && $value == $parent) {
array_shift($temp_path);
array_pop($temp_path);
return $temp_path;
}
if (is_array($value) && count($value) > 0) {
$res_path = get_nested_path(
$parent, $value, $temp_path);
if ($res_path != null) {
return $res_path;
}
}
}
}
return null;
}
function insertItem(&$array, $path, $toInsert)
{
$target = &$array;
foreach ($path as $key) {
if (array_key_exists($key, $target))
$target = &$target[$key];
else throw new Exception('Undefined path: ["' . implode('","', $path) . '"]');
}
$target['children'] = isset($target['children']) ? $target['children'] : [];
$target['children'][$toInsert['name']] = $toInsert;
return $target;
}
Here's my take on what I believe is the desired output:
function buildTree(array $items): ?array {
// Get a mapping of each item by ID, and pre-prepare the "children" property.
$idMap = [];
foreach ($items as $item) {
$idMap[$item['id']] = $item;
$idMap[$item['id']]['children'] = [];
}
// Store a reference to the treetop if we come across it.
$treeTop = null;
// Map items to their parents' children array.
foreach ($idMap as $id => $item) {
if ($item['parent'] && isset($idMap[intval($item['parent'])])) {
$parent = &$idMap[intval($item['parent'])];
$parent['children'][] = &$idMap[$id];
} else if ($item['parent'] === null) {
$treeTop = &$idMap[$id];
}
}
return $treeTop;
}
This does two array cycles, one to map up the data by ID, then one to assign children to parents. Some key elements to note:
The build of $idMap in the first loop also effectively copies the items here so we won't be affecting the original input array (Unless it already contained references).
Within the second loop, there's usage of & to use references to other items, otherwise by default PHP would effectively create a copy upon assignment since these are arrays (And PHP copies arrays on assignment unlike Objects in PHP or arrays in some other languages such as JavaScript). This allows us to effectively share the same array "item" across the structure.
This does not protect against bad input. It's possible that invalid mapping or circular references within the input data could cause problems, although our function should always just be performing two loops, so should at least not get caught in an infinite/exhaustive loop.
For a social media login with Xing, I want to verificate the data send from XING as described in https://dev.xing.com/plugins/login_with/docs#signature-verification
My page sends data as JSON-Array to a PHP function
public function indexAction(Request $request)
{
dump($request->request);
$xingData=\GuzzleHttp\json_decode($request->request->get('xingData'),true);
$xingUser=$xingData['user'];
$xingError=$xingData['error'];
$xingCookie=$request->request->get('xingCookie');
$xingSerialised= $this->flatArray($xingUser);
dump($xingUser);
dump($xingError);
dump($xingCookie);
dump($xingSerialised);
//$xingUserhash=hash_hmac('sha256',$xingSerialised,$this->container->getParameter('xing_salt'));
//dump($xingUserhash);
die(0);
return $this->render('base.html.twig', array(
));
}
function flatArray($fullArray = array())
{
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($fullArray));
$elements = array();
foreach($iterator as $element) {
$elements[] = $iterator->key().$element;
}
return $elements;
}
The JSON-decode-Function makes this array:
XingController.php on line 36:
array:9 [▼
"id" => "MyId"
"first_name" => "MyName"
"last_name" => "MyLastName"
"display_name" => "MyDisplayName"
"active_email" => "my#email.de"
"permalink" => "https://www.xing.com/profile/MyName"
"business_address" => array:4 [▼
"street" => "MyStreet"
"city" => "MyCity"
"province" => "MyProvince"
"country" => "DE"
]
"photo_urls" => array:1 [▼
"maxi_thumb" => "https://www.xing.com/image/image.jpg"
]
"professional_experience" => array:1 [▼
"primary_company" => array:3 [▼
"name" => "MyCompanyName"
"title" => "Inhaber"
"industry" => "CONSUMER_SERVICES"
]
]
]
The problem is, I get
XingController.php on line 39:
array:14 [▼
0 => "idMyId"
1 => "first_nameMyName"
2 => "last_nameMyLastName"
3 => "display_nameMyDisplayName"
4 => "active_emailmy#email.de"
5 => "permalinkhttps://www.xing.com/profile/MyName"
6 => "streetMyStreet"
7 => "cityMyCity"
8 => "provinceMyProvince"
9 => "countryDE"
10 => "maxi_thumbhttps://www.xing.com/image/image.jpg"
11 => "nameMyCompanyName"
12 => "titleInhaber"
13 => "industryCONSUMER_SERVICES"
]
But I wanted the full parents with it, for example:
6 => "business_addressstreetMyStreet"
Thanks for any idea/help!
Try something like:
function flatArray($fullArray = array())
{
$elements = array();
foreach($fullArray as $key => $value) {
if (is_array($value)) {
$elements[] = $this->flatArray($value);
} else {
$elements[] = sprintf('%s%s', $key, $value);
}
}
return $elements;
}
Probably the recursion could be improved
Hope this help
Version with full recursion:
function flatArray($fullArray = array(), $parentkey=null)
{
$elements = array();
foreach($fullArray as $key => $value) {
if (is_array($value)) {
$elements = array_merge($elements, $this->flatArray($value, $parentkey.$key));
} else {
$elements[] = sprintf('%s%s%s', $parentkey, $key, $value);
}
}
return $elements;
}
I have the following structure array:
array:6 [▼
"593a4331b25f428814000035" => array:8 [▶]
"593a4331b25f428814000036" => array:8 [▶]
"593a4331b25f428814000037" => array:8 [▶]
"593a4331b25f428814000038" => array:8 [▼
"_id" => MongoId {#238 ▶}
"object_id" => "593a4331b25f428814000034"
"parameter_id" => "59398f5ab25f424016000029"
"value" => "1"
"children" => []
"parent_id" => "593a4331b25f428814000037"
"type" => "2"
"prefix" => "object"
]
"593a4331b25f428814000039" => array:8 [▶]
"593a4331b25f42881400003a" => array:8 [▶]
]
As you can see 3-th element of array has parent 593a4331b25f428814000037, where identificator is element in the same array.
How to put this element 593a4331b25f428814000038 inside parent in children?
In result I need to get:
"593a4331b25f428814000037" => array:8 [▼
"_id" => MongoId {#238 ▶}
"object_id" => "593a4331b25f428814000034"
"parameter_id" => "59398f5ab25f424016000029"
"value" => "1"
"children" => [ 0 => array("_id" => MongoId {#238 ▶}
"object_id" => "593a4331b25f428814000034"
"parameter_id" => "59398f5ab25f424016000029"
"value" => "1"
"children" => []
"parent_id" => "593a4331b25f428814000037"
"type" => "2"
"prefix" => "object")]
"parent_id" => "593a4331b25f428814000037"
"type" => "2"
"prefix" => "object"
]
I tried this way:
public function recursion($data){
foreach ($data as $k => $value) {
if (is_array($value['children']) && count($value['children']) > 0) {
$list[$k] = $value;
$list[$k]["children"] = $this->getChildren($all, $value['children']);
} else {
$list[$k] = $value;
}
}
return $list;
}
private function getChildren($all, $childs)
{
$list = [];
foreach ($childs as $k => $child) {
if (is_array($all[$child]['children'])) {
$tmpArray = $all[$child];
$tmpArray['children'] = $this->getChildren($all, $all[$child]['children']);
} else {
$tmpArray = $all[$child];
}
$list[] = $tmpArray;
}
return $list;
}
But it works incorrect
You can use array_reduce like:
$array = array_reduce($myArray, function ($carry, $item) {
if (empty($item['parent_id'])) {
$carry[$item['object_id']] = $item;
} else {
$carry[$item['parent_id']]['children'][] = $item;
}
return $carry;
}, []);
var_dump($array);
In this example I supposed the parent_id is empty for those items which doesn't belong to a another item. You can change that with isset in case there is no parent_id key
To put it right lets say I have an array stores-
array:3 [▼
0 => "store3"
1 => "store"
2 => "store2"
]
This is the array which holds values.
Other array products holds all the data:-
array:2 [▼
0 => array:2 [▼
"store" => "store"
"product" => "1"
]
1 => array:2 [▼
"store" => "store"
"product" => "4"
]
2 => array:2 [▼
"store" => "store2"
"product" => "2"
]
3 => array:2 [▼
"store" => "store2"
"product" => "3"
]
4 => array:2 [▼
"store" => "store3"
"product" => "7"
]
5 => array:2 [▼
"store" => "store3"
"product" => "11"
]
]
What I want is that a value is picked from stores array eg store3 then it is compared with products array and being searched and extract all the arrays inside products array which has the store value store3 and store it in another new array named store3
I have tried to do it but it was very wrong I mean it didn't work! I will post it if anyone say so but can anyone accomplish this?
My work:-
$temp = array();
for($i=0; $i<count($stores); $i++)
{
//$stores[$i] = array();
foreach($products as $p)
{
if(session($stores[$i]) == $p['store'])
{
if(count(session($stores[$i])) == 0)
{
$temp['product'] = $p['product'];
session($stores[$i])->push($temp['product']);
}
else if(!in_array($p['product'],$stores[$i]))
{
$temp['product'] = $p['product'];
session($stores[$i])->push($temp['product']);
}
}
}
}
Do it like below:-
$final_array = array();
foreach($array1 as $arr){
foreach($array2 as $arr2){
if($arr == $arr2['store']){
$final_array[$arr]['product'][] = $arr2['product'];
}
}
}
echo "<pre/>";print_r($final_array);
Output:-https://eval.in/752498
using array_walk
$array = [];
array_walk($products, function ($value, $key) use ($stores, &$array) {
$array[$value['store']][] = $value['product'];
});
live sample : https://3v4l.org/BfeMm
using array filter
$store = 'store1';
$products = array_filter($products, function($product) use($store) {
return (isset($product['store']) and $product['store'] == $store);
});
var_dump($products);
https://eval.in/752508
I made a simple function to find products in store
function searchProduct($products,$storeName){
$results =array();
foreach($products as $product){
if(in_array($storeName,array_values($product)))
$results[] = $product;
}
return $results;
}
print_r(searchProduct($products,'store3'));
This question already has answers here:
How to Flatten a Multidimensional Array?
(31 answers)
Closed 6 years ago.
A little help needed here
I have this array:
0 => array:4 [▼
"StudentName" => "John Doe "
"StudentNumber" => "2055222"
0 => array:1 [▼
"Test" => 33.5
]
1 => array:1 [▼
"Assignment" => 57.0
]
]
1 => array:4 [▼
"StudentName" => "Jane Doe"
"StudentNumber" => "5222112"
0 => array:1 [▼
"Test" => 47.0
]
1 => array:1 [▼
"Assignment" => 68.0
]
]
2 => array:4 [▼
"StudentName" => "Alice Doe"
"StudentNumber" => "5555555"
0 => array:1 [▼
"Test" => 0.0
]
1 => array:1 [▼
"Assignment" => 67.0
]
]
And I want to convert it to look like this:
0 => array:4 [▼
"StudentName" => "John Doe "
"StudentNumber" => "20160022"
"Test" => 33.5
"Assignment" => 57.0]
Is there some sort of php function I can use?
Edit: Added more examples to help you think of a better solution
There's no native array flatten in PHP but you can do this:
function array_flatten($array) {
if (!is_array($array)) {
return false;
}
$result = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$result = array_merge($result, array_flatten($value));
} else {
$result[$key] = $value;
}
}
return $result;
}
Found here
Also numerous other approaches here: How to Flatten a Multidimensional Array?
You can do it using like this (not tested):
$arr = Array();
foreach($oldArr AS $k => $v){
if(is_array($v)){
foreach($v AS $a => $b){
$arr[$a] = $b;
}
}else{
$arr[$k] = $v;
}
}
This should work:
// Store your new array in a separate variable to avoid key conflicts with trying to us unset() in a loop
$new_array = array();
foreach($original_array as $k=>$v)
{
if(is_array($v)) // check if element is an array
{
foreach($v as $k2=>$v2) // loop the sub-array and add its keys/indexes to the new array
{
$new_array[$k2] = $v2;
}
}
else
{
// the element is not a sub-array so just add the key and value to the new array
$new_array[$k] = $v;
}
}