I'm not able to do get it right. If I have an array notation like this:
['email', 'privacy.language', 'privacy.phonenr', 'privacy.user.id']
I would like to get an array that looks like this:
[
0 => 'email',
'privacy' => [
0 => 'language',
1 => 'phonenr',
'user' => [
0 => 'id'
]
],
]
My current approach looks like this:
protected function undotArray(array $fields)
{
$array = [];
foreach ($fields as $key => $field) {
$keys = explode('.', $field);
if(count($keys) == 1){
$array[] = $keys[0];
continue;
}
if(count($keys) == 2){
$array[$keys[0]][] = $keys[1];
continue;
}
if(count($keys) == 3){
$array[$keys[0]][$keys[1]][] = $keys[2];
}
}
return $array;
}
But as you see the "if conditions" are similar and they repeat themselves, depending on the number of dots. I feel like that I need to use a recursive function here, but I don't know how.
Related
I have an array like the below:
$arrays = [
'a' => [
'name' => "Name 1",
'age' => "99",
'add' => ""
],
'b' => [
'name' => "Name 2",
'age' => "99",
'add' => "Add2"
],
'c' => [
'name' => "Name 3",
'age' => "99",
'add' => "Add3"
],
'd' => [
'name' => "",
'age' => "",
'add' => "Add4"
]
];
I want to get a result like:
$res = [
'a' => ['add'],
'd' => ['name','age']
];
I have tried with the below code, but it returns 1.
$status = array_walk_recursive($arrays, function($v, $k) {
global $output;
if (empty($v) && $v !== 0)
$output[$k] = $v;
});
I want to do it without using any loops because my real input array is very large and I am concerned with performance.
If your input is always of a fixed depth, you can map the existing values to the keys of all empty items:
$output = array_map(function($row) {
return array_keys(array_filter($row, function ($e) {
return empty($e) && $e !== 0;
}));
}, $arrays);
The outer function runs for each "row", the value of which is then converted into a list of all keys with empty values (excluding zeroes, as in the question).
This will keep the outer keys B & C as empty arrays, so if you want them to be removed as well then run another iteration of array_filter over the result:
$output = array_filter($output)
See https://3v4l.org/c23ZB
As mentioned in the comments, there are still several loops going on here, they're just not as visible in the code. A regular foreach loop might end up being a lot easier to read, and possibly perform faster as well.
You can use next combination of array_walk & array_filter:
$result = [];
array_walk(
$arrays,
function($el, $key) use(&$result) {
$empty = array_filter($el, function($el){return $el == "";});
$empty_keys = array_keys($empty);
if (count($empty_keys)) $result[$key] = $empty_keys;
}
);
Try it here
This is another way to achieve your desired output.
$result = [];
foreach($arrays as $key => $value) {
$empty_arr = array_filter($value, function ($ele) {
return empty($ele);
});
$empty_arr_keys = array_keys($empty_arr);
if(!empty($empty_arr_keys)) $result[$key] = $empty_arr_keys;
}
print_r($result);
#iainn's answer can be sharpened up by calling !strlen() on the deep values.
Code: (Demo)
var_export(
array_filter(
array_map(
fn($row) => array_keys(
array_filter(
$row,
fn($v) => !strlen($v)
)
),
$array
)
)
);
But you will end up making fewer iterations and writing cleaner, more intuitive/readable code if you use classic loops. This is how I would write it in my own project:
Code: (Demo)
$result = [];
foreach ($array as $rowKey => $row) {
foreach ($row as $key => $value) {
if (!strlen($value)) {
$result[$rowKey][] = $key;
}
}
}
var_export($result);
I have an array, similar to the following:
$array = [
['file' => 1, 'status' => 'pending'],
['file' => 2, 'status' => 'pending'],
];
What I want to do is to replace the statuspart, where the file is 1
I'm not sure if i can do this in a simple array, or using an in-built array_ method
I have something liek the following so far:
$data = [];
foreach($array as $arr => $val) {
if ($val['file'] == 1) {
$data['status'] = 'updated';
}
}
You're close, but as $data is array of arrays, you need to provide $arr as top-level key and even get rid of second array:
foreach($array as $arr => $val) {
if ($val['file'] == 1) {
$array[$arr]['status'] = 'updated';
}
}
This should give you an idea of how to tackle that.
if ($array[0]['file'] == 1) {
$array[0]['whatever'] = $array[0]['status'];
unset($array[0]['status']);
}
The 0 can ofcourse be changed by i in a loop if you wish to cycle through the entire array.
i have many api service with response
[
'brand_name' => Adidas,
'item_count' => 24
]
and
[
'brand_name' => Nike,
'count_item' => 254
]
and
[
'name' => Reebok,
'count_all' => 342
]
how to transform it to 1 standard? oop please
['brand' => $value, 'cnt' = $count]
If they are in the same order, just combine your keys with the values:
$array = array_combine(['brand', 'cnt'], array_values($array));
If not, just match and replace:
$array = array_combine(
preg_replace(['/.*name.*/', '/.*count.*/'], ['brand', 'cnt'], array_keys($array)),
$array);
If it could be brand, name or brand_name, etc. Then use an OR |:
/.*(name|brand).*/
If you know all of the possible combinations, then:
$replace = ['brand_name' => 'brand', 'name' => 'brand',
'item_count' => 'cnt', 'count_item' => 'cnt', 'count_all' => 'cnt'];
$array = array_combine(str_replace(array_keys($replace), $replace, array_keys($array)),
$array);
One of these should get you on the right path.
To transform many array into one standard like above, you need to think of common criteria that can apply to all arrays. In your case, I can see that all arrays have 2 keys, with the first keys contain a word "name" so I can use it as a criterion for "brand". Then there is second keys contain a word "count" used as a criterion for "cnt".
$new_array = [];
foreach($array as $item) {
$brand = '';
$cnt = '';
foreach($item as $key => $value) {
if(strpos($key, 'name') !== false) {
$brand = $value;
}
elseif(strpos($key, 'count') !== false) {
$cnt = $value;
}
// more conditions
}
$new_array[] = ['brand' => $brand, 'cnt' => $cnt];
}
Suppose you have another array:
[
'brand' => 'Danbo',
'total' => 200,
]
Since this is an exception, you need to make another condition:
elseif(strpos($key, 'brand') !== false) {
$brand = $value;
}
elseif(strpos($key, 'total') !== false) {
$cnt = $value;
}
or append to existing conditions:
if(strpos($key, 'name') !== false || strpos($key, 'brand') !== false) {
$brand = $value;
}
elseif(strpos($key, 'count') !== false || strpos($key, 'total') !== false) {
$cnt = $value;
}
This seems simple, but has perplexed me. I need to get $env to look like $desired result.
I tried using explode and foreach loops in a multitude of ways but keep getting stuck.
$env = [
["mysql_user"=>"user var"],
["mysql_pass"=>"password var"],
["rabbit_list_one"=>"listone var"],
["rabbit_list_two"=>"listtwo var"],
["system_var_main_deep"=>"deep this"],
["system_var_main_that"=>"deep that"]
];
$desiredResult = [
"mysql" => [
"user" => "user var",
"pass" => "password var"
],
"rabbit" => [
"list" => [
"one" => "listone var",
"two" => "listtwo var"
]
],
"system" => [
"var" => [
"main" => [
"deep" => "deep this",
"that" => "deep that"
]
]
]
];
Double-check the formatting on $env, because you're showing arrays inside of the $env array rather than just key/value pairs. Assuming your input is correct, and there are actually inner arrays, this should work:
$out = [];
foreach ($env as $piece) {
foreach ($piece as $key => $value) {
$key_full = explode('_', $key);
$key_last = array_pop($key_full);
$pointer = &$out;
foreach ($key_full as $key_level) {
if (!isset($pointer[$key_level])) {
$pointer[$key_level] = [];
}
$pointer = &$pointer[$key_level];
}
$pointer[$key_last] = $value;
}
}
Based on Netrilix's answer, this was the final solution. This handled the situation where FOO_BAR_BAX and FOO_BAR were both set. We opted to always take the array over the string. Thank you all for all your help!
public function build() {
$config = $_ENV;
$out = [];
foreach ($config as $key => $value) {
$key_full = explode('_', $key);
$key_last = strtolower(array_pop($key_full));
$pointer = &$out;
foreach ($key_full as $key_level) {
$key_level = strtolower($key_level);
if (!isset($pointer[$key_level])) {
$pointer[$key_level] = [];
}
$pointer = &$pointer[$key_level];
}
$pointer = !is_array($pointer) ? [] : $pointer;
if (!isset($pointer[$key_last])) {
$pointer[$key_last] = $value;
}
}
return $out;
}
I'm sure there is an easy way to do this, but I can't think of it right now. Is there an array function or something that lets me search through an array and find the item that has a certain property value? For example:
$people = array(
array(
'name' => 'Alice',
'age' => 25,
),
array(
'name' => 'Waldo',
'age' => 89,
),
array(
'name' => 'Bob',
'age' => 27,
),
);
How can I find and get Waldo?
With the following snippet you have a general idea how to do it:
foreach ($people as $i => $person)
{
if (array_key_exists('name', $person) && $person['name'] == 'Waldo')
echo('Waldo found at ' . $i);
}
Then you can make the previous snippet as a general use function like:
function SearchArray($array, $searchIndex, $searchValue)
{
if (!is_array($array) || $searchIndex == '')
return false;
foreach ($array as $k => $v)
{
if (is_array($v) && array_key_exists($searchIndex, $v) && $v[$searchIndex] == $searchValue)
return $k;
}
return false;
}
And use it like this:
$foundIndex = SearchArray($people, 'name', 'Waldo'); //Search by name
$foundIndex = SearchArray($people, 'age', 89); //Search by age
But watch out as the function can return 0 and false which both evaluates to false (use something like if ($foundIndex !== false) or if ($foundIndex === false)).
function findByName($array, $name) {
foreach ($array as $person) {
if ($person['name'] == $name) {
return $person;
}
}
}
function getCustomSearch($key) {
foreach($people as $p) {
if($p['name']==$searchKey) return $p
}
}
getCustomSearch('waldo');