Flatten array to use array_key_exists on a multi-dimensional array - php

I have a php array that looks like this...
(
[name] => Test
[age] => 50
[items] => Array
(
[23456] => Array
(
[id] => 12
)
[3345] => Array
(
[id] => 344
[status] => stock
)
[2236] => Array
(
[id] => 876
)
)
)
I am trying to search for any mention of status in the items section of the array.
I have tried using array_key_exists like this...
array_key_exists('test',$arr);
But this is giving me false, as an alternative I was thinking of flattening the array somehow and then searching to make it work with my array.
Is this the best choice?

You can use also use array_column() to see if the key exists :
$exist = !empty(array_column($arr['items'], 'status'));
But, #Barmar answer could be more efficient on large arrays.

Loop over the items array and check each item.
function status_exists_in_items($arr) {
foreach ($arr['items'] as $item) {
if (array_key_exists('status', $item)) {
return true;
}
}
return false;
}

Just filter the array and handle just those items, that have a status key.
$data = [
'name' => 'Test',
'age' => 50,
'items' => [
23456 => [
'id' => 12,
],
3345 => [
'id' => 344,
'status' => 'stock',
],
2236 => [
'id' => 876,
],
],
];
$result = array_filter($data['items'], function($item) {
if (isset($item['status'])) return $item;
});
var_dump($result);
Results into:
array(1) {
[3345] => array(2) {
["id"] => int(344)
["status"] => string(5) "stock"
}
}
This solution could be slow (as every function that handles native arrays) depending on how big your array is. You can work with filter iterators, which work as yields and do not consume as much memory as arrays do.

Related

Defining values to specific indexes of a multidimensional array [duplicate]

This question already has answers here:
How to access and manipulate multi-dimensional array by key names / path?
(10 answers)
Closed 1 year ago.
I'm trying to create a function that will assign new values to a specific indexes in a multidimensional array:
I Have the array that looks like this:
data[i]['checkin'];
data[i]['checkout'];
data[i]['content'][x]['price'];
data[i]['content'][x]['profit'];
data[i]['content'][x]['exchangerate'];
first parameter of my function will get the array, And second parameter will get the indexes that I want to redefine:
For example:
function defineNewValues(&$arr, $keys) {
//logic
}
Call the function:
defineNewValues($myArray, [
'data.*.content.*.price' => 0,
'data.*.content.*.profit => 0,
]);
Im beliving that recursion is the key for my problem ,
But not really know how to solve it.
Thank You.
could something like this be okay?
I only ask you to study this code not to implement it, for the simple reason that in the future you may have the same type of problem.
function setValue($key,$value,&$array){
$find_parts = explode(".", $key);
$find = $find_parts[0]??null;
if ($find!=null){
if ($find == "*"){
array_shift($find_parts);
foreach($array as &$sub_array){
setValue(implode(".",$find_parts),$value,$sub_array);
}
}else{
if (count($find_parts)>1){
if (array_key_exists($find,$array)){
array_shift($find_parts);
setValue(implode(".",$find_parts),$value,$array[$find]);
}
}else{
if (array_key_exists($find,$array)){
$array[$find] = $value;
}
}
}
}
}
function defineNewValues(&$arr, $keys) {
foreach($keys as $key=>$value){
setValue($key,$value,$arr);
}
}
$myArray=[
"data"=>[
"a"=>[
"content"=>[
"aa"=>[
"price" => 3,
"profit" => 2,
"other" => 1
],
"ab"=>[
"price" => 3,
"profit" => 2,
"other" => 2
]
]
],
"b"=>[
"content"=>[
"ba"=>[
"price" => 3,
"profit" => 2,
"other" => 4
],
"bb"=>[
"price" => 3,
"profit" => 2,
"other" => 5
]
]
],
]
];
defineNewValues($myArray, [
"data.*.content.*.price" => 0,
"data.*.content.*.profit" => 0,
]);
print_r($myArray);
/* OUTPUT
Array
(
[data] => Array
(
[a] => Array
(
[content] => Array
(
[aa] => Array
(
[price] => 0
[profit] => 0
[other] => 1
)
[ab] => Array
(
[price] => 0
[profit] => 0
[other] => 2
)
)
)
[b] => Array
(
[content] => Array
(
[ba] => Array
(
[price] => 0
[profit] => 0
[other] => 4
)
[bb] => Array
(
[price] => 0
[profit] => 0
[other] => 5
)
)
)
)
)
*/
Because the keys you want to replace only occur at one level of the data, the solution doesn't really need to take the entire array structure into account. You can just replace every price and profit key.
array_walk_recursive($example, function(&$value, $key) {
if (in_array($key, ['price', 'profit'])) {
$value = 0;
}
});
Based on your comment on the other answer, my opinion on the "correct and professional way" is that we should try to solve the problem in the simplest way possible, because simple solutions are easy to maintain.

PHP Unset array values

I have an array and have keys in separate array. I need to unset the array.
Array
(
[0] => RequestDocument
[1] => Orders
[2] => Order
[3] => OrderProducts
[4] => OrderProduct
)
How to loop array to make unset like below.
unset(["RequestDocument"]["Orders"]["Order"]["OrderProducts"]["OrderProduct"]);
Please help.
Thanks,
Sarnitha
I advice to use array_filter function in this case:
<?php
$x = [
0 => "RequestDocument",
1 => "Orders",
2 => "Order",
3 => "OrderProducts",
4 => "OrderProduct",
];
$filter = [
"RequestDocument",
"Orders",
// "Order",
"OrderProducts",
"OrderProduct",
];
$y = array_filter($x, function ($el) use ($filter) {
return !in_array($el, $filter);
});
Test here PHP online code

Convert a multidimensional array (php) [duplicate]

This question already has answers here:
Generate an associative array from an array of rows using one column as keys and another column as values
(3 answers)
Closed 3 years ago.
im currently struggling with converting this array in PHP to a more simplified one. This is my array to start with stored in $array:
[0] => Array
(
[name] => name-1
[value] => xXX
)
[1] => Array
(
[name] => name-2
[value] => YYY
)
I would like to transfrom this array to this simplified one $array_new:
[0] => Array
(
[name-1] => xXX
)
[1] => Array
(
[name-2] => YYY
)
I sadly don't know were to start... Could somebody help me out?
Edit: After I converted the array via array_column() oder foreach loop I still cant get the right data with $array_new['name-2'];
You can use array-column to do that. The documentation said:
array_column ( array $input , mixed $column_key [, mixed $index_key = NULL ] ) : array
So do:
$first_names = array_column($array, 'value', 'name');
Live example: 3v4l
Question
Alright, this is a question I see a lot of beginners deal with.
Just be a little creative:
Answer
//Let's get your old array:
$old = [
0 => [
'name' => 'name-1',
'value' => 'xXX'
],
1 => [
'name' => 'name-2',
'value' => 'YYY'
]
];
//Let's create an array where we will store the new data:
$result = [];
foreach($old as $new) { //Loop through
$result[$new['name']] = $new['value']; //Store associatively with value as value
}
var_dump($result);
Result:
Array[2] => [
[name-1] => xXX,
[name-2] => YYY
];
Using a foreach:
<?php
$items =
[
[
'plant' => 'fern',
'colour' => 'green'
],
[
'plant' => 'juniper',
'colour' => 'blue'
]
];
foreach($items as $item) {
$output[][$item['plant']]=$item['colour'];
}
var_dump($output);
Output:
array(2) {
[0]=>
array(1) {
["fern"]=>
string(5) "green"
}
[1]=>
array(1) {
["juniper"]=>
string(4) "blue"
}
}

How to group an array of associative arrays and declare custom keys?

Can someone help me with converting a php array in a grouped format? I am trying to group them by id. I would like to have the following array converted:
$Arr1=Array
(
0 => Array
(
"id" => "4123",
"test_number" => "1",
"sat_total" => "1050"
),
1 => Array
(
"id" => "4123",
"test_number" => "2",
"sat_total" => "1130"
),
2 => Array
(
"id" => "4123",
"test_number" => "3",
"sat_total" => "1120"
),
3 => Array
(
"id" => "5555",
"test_number" => "1",
"sat_total" => "1130"
),
4 => Array
(
"id" => "5555",
"test_number" => "2",
"sat_total" => "1160"
)
);
into this:
$Arr2=Array
(
0 => Array
(
"id" => "4123",
"Score1" => "1050",
"Score2" => "1130",
"Score3" => "1120"
),
1 => Array
(
"id" => "5555",
"Score1" => "1130",
"Score2" => "1160"
)
);
I have tried a little bit, but can't seem to find how to make it work.
You only need to iterate your rows of data, determine if each row is the first occurring id value or not, then either declare the initial values, or add a variably keyed element to the group. When the loop finishes, call array_values() to reindex the array (remove the temporary keys).
Code: (Demo)
$Arr1=[
["id" => "4123", "test_number" => "1", "sat_total" => "1050"],
["id" => "4123", "test_number" => "2", "sat_total" => "1130"],
["id" => "4123", "test_number" => "3", "sat_total" => "1120"],
["id" => "5555", "test_number" => "1", "sat_total" => "1130"],
["id" => "5555", "test_number" => "2", "sat_total" => "1160"]
];
foreach ($Arr1 as $set) {
if (!isset($result[$set['id']])) {
$result[$set['id']] = ['id' => $set['id'], 'Score1' => $set['sat_total']];
} else {
$result[$set['id']]['Score' . sizeof($result[$set['id']])] = $set['sat_total'];
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'id' => '4123',
'Score1' => '1050',
'Score2' => '1130',
'Score3' => '1120',
),
1 =>
array (
'id' => '5555',
'Score1' => '1130',
'Score2' => '1160',
),
)
This method will find the scores matching the $id.
It uses three array_intersects to match all the values correct.
This method will only loop the number of unique IDs, in your case two times.
Plus the times to create the score keys.
I do agree with what ggorlen says about the keys. That will also create a more efficient code.
$ids = array_column($Arr1, "id");
$sat = array_column($Arr1, "sat_total");
foreach(array_unique($ids) as $id){
$new[$id] = ["id" => $id];
$tmp = array_values(array_intersect_key($sat,array_intersect_key($Arr1, array_intersect($ids, [$id]))));
for($i=1;$i<=count($tmp);$i++) $new[$id]["Score" . $i] = $tmp[$i-1];
}
var_dump($new);
https://3v4l.org/ag3To
The output is an associative array with id as key.
You can use array_values if you want to make it indexed.
Just to show how much more efficient the code can be with one score array.
This is what it would look like:
$ids = array_column($Arr1, "id");
$sat = array_column($Arr1, "sat_total");
foreach(array_unique($ids) as $id){
$new[] = ["id" => $id, "scores" => array_values(array_intersect_key($sat,array_intersect_key($Arr1, array_intersect($ids, [$id]))))];
}
var_dump($new);
https://3v4l.org/mdA0W
$arr2 = [];
$i = 0;
$length = count($arr1);
do {
$builder = $arr1[$i];
// grab the first item from the original array
$builder = [
// set its initial properties
'id' => $arr1[$i]['id'],
'Score1' => $arr1[$i]['sat_total'],
];
// initialise the subsequent score number
$testNumber = 2;
// prepare to look ahead in the original array for a matching id
while (($i + 1) < $length) { // only look ahead if it is possible to
if ($arr1[$i + 1]['id'] == $builder['id']) {
// did you find a matching id? if so, let's set the subsequent score
$builder["Score$testNumber"] = $arr1[$i + 1]['sat_total'];
$testNumber++; // increase the next score number
$i++; // increase the original array index
} else {
// no luck? let's go forwards and set the next record
break;
}
}
$arr2[] = $builder; // set the built record into the new array
$i++; // move the pointer forwards
} while ($i < $length); // as long as there are items ahead
Not often you get to use a do-while. But it works :)
Feed it your original array $arr1 and $arr2 will be set.
It works by looking forward for matching ids. This solution assumes your original array is ordered by id! So unless you trust the input - don't use this solution!
Otherwise this is a simple, fast, and fairly readable solution to what looks to me like a school exercise?
If you want something that is safe, the other solutions here are suitable.
I'm not sure this structure is ideal--it seems like your keys "Score1", "Score2" etc would be best as an array like scores => [1050, 1130, ...] and it feels like the ids should be keys in the result array. But in any case, this gives your requested output:
$res = [];
foreach ($arr as $e) {
if (!array_key_exists($e['id'], $res)) {
$res[$e['id']] = [];
}
$res[$e['id']]["Score".(count($res[$e['id']])+1)] = $e['sat_total'];
}
$count = 0;
foreach ($res as $k => $v) {
$res[$k]['id'] = $k;
$res[$count++] = $res[$k];
unset($res[$k]);
}
print_r($res);
Output
Array
(
[0] => Array
(
[Score1] => 1050
[Score2] => 1130
[Score3] => 1120
[id] => 4123
)
[1] => Array
(
[Score1] => 1130
[Score2] => 1160
[id] => 5555
)
)
Note that I did two passes which is a little verbose, but taking the time to key in the data ids into the array in the first pass should improve a linear search through the array for each element into O(1) hashing, so I think it's worth the extra loop block.

PDO - array_map return objects ids in keys

hey i have array with returned keys
$temp = $sth->fetchAll(PDO::FETCH_ASSOC);
my result looks like this:
[0] => [
'id' = 11,
'title' => 't1'
]
[1] => [
'id' = 12,
'title' => 't2'
]
if i want to return ids as key i call something like this:
$temp = array_map(function($v){return $v[0];}, $sth->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC));
and my resoult looks like this:
[11] => [
'title' => 't1'
]
[12] => [
'title' => 't2'
]
how to return array of objects by ID? when i do this i dont have methods in object...
$temp = array_map(function($v){return $v[0];}, $sth->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_CLASS));
I will do a bit easier code like below:-
$fianl_array = array_combine(array_column($temp,'id'),$temp);
Output:- https://eval.in/993728
Reference:-
array_column()
array_combine()
Using foreach:
foreach($input as $k=>$v){
$output[$v['id']] = array('title'=>$v['title']);
}
print_r($output);
Just had to add this as an answer as I believe it's the shortest way of doing this, using array_column() with a third parameter which is the key you want the data indexed by. If you use null as the second parameter, it will index all the data by 'id', you could instead use 'title' and this will only index the title columns by ID...
$output = array_column($temp,null,'id');
Which gives...
Array
(
[11] => Array
(
[id] => 11
[title] => t1
)
[12] => Array
(
[id] => 12
[title] => t2
)
)

Categories