I have this function, where a array_filter function is included:
$var = "test";
function mainFunction() {
global $var;
$myNewArray = array();
$data = array("a", "b", "c");
array_filter($data, function ($value) {
global $myNewArray;
$myNewArray[] = $value;
});
print_r($myNewArray); // TEST OUTPUT
}
mainFunction();
Problem:
My test output myNewArray is empty.
I know that my array_filter function is senless at the moment until I check no values.
But only for testing, I would like to use it, to create a newArray. But this doesn't work. Where is my mistake?
UPDATE
I updated my code:
function mainFunction() {
global $var;
$myNewArray = array();
$data[] = array("id" => "1", "content" => "Hello");
$data[] = array("id" => "2", "content" => "World");
$myNewArray = array_filter($data, function ($value) {
if ($value['content'] == "World") {
return $value['content'];
}
});
print_r($myNewArray); // TEST OUTPUT
}
mainFunction();
This works, but not correctly.
I would like to save only the content value.
But my $myNewArray looks like this:
Array
(
[0] => Array
(
[id] => 2
[content] => World
)
)
Instead of
Array
(
[0] => Array
(
[content] => World
)
)
I would combine array_filter and array_map for this.
$data[] = array("id" => "1", "content" => "Hello");
$data[] = array("id" => "2", "content" => "World");
// filter the data
$data = array_filter($data, fn ($value) => $value['content'] === 'World');
// map the data
$data = array_map(fn ($value) => ['content' => $value['content']], $data);
// reset indexes
$data = array_values($data);
print_r($data);
Example: https://phpize.online/s/9U
Everything seems to work fine.
<?php
$data = [];
$data[] = array("id" => "1", "content" => "Hello");
$data[] = array("id" => "2", "content" => "World");
$filtered_data = array_filter($data, function($value) {
return $value['content'] == "World";
});
print_r($filtered_data);
The output is just like expected:
Array (
[1] => Array
(
[id] => 2
[content] => World
)
)
But if you want to leave only some fields in resulting array, array_filter will not help you (at least without a crutch).
You may want to iterate source array and filter it by yourself.
<?php
$data = [];
$data[] = array("id" => "1", "content" => "Hello");
$data[] = array("id" => "2", "content" => "World");
$filtered_data = [];
foreach($data as $v) {
if($v['content'] == "World")
$filtered_data[] = ["content" => $v['content']];
}
print_r($filtered_data);
The output then would be:
Array (
[0] => Array
(
[content] => World
)
)
You want two different things :
filter your array (keep only some elements)
map your array (change the value of each element)
Filter your array
On your second attempt you've done it right but array_filter callback function expect a boolean as the return value. It will determine wherever array_filter need to keep the value or not.
Map your array
You need to remove all value on each element except the "content" value. You can use array_map to do that.
function mainFunction() {
$data[] = array("id" => "1", "content" => "Hello");
$data[] = array("id" => "2", "content" => "World");
$myNewArray = array_filter($data, function ($value) {
if ($value['content'] == 'World') {
return true;
}
return false;
});
// myNewArray contains now the willing elements, but still don't have the willing format
/* myNewArray is [
0 => [
'id' => '2',
'content' => 'World'
]
]*/
$myNewArray = array_map($myNewArray, function($value){
return [
'content' => $value['content']
];
});
// myNewArray contains now the willing elements with the willing format
/* myNewArray is [
0 => [
'content' => 'World'
]
] */
}
mainFunction();
In mainFunction you are not using $myNewArray as global so it's only in the scope, but in the array_filter function you are using global $myNewArray;
$var = "test";
$myNewArray; // global array
function mainFunction() {
global $var, $myNewArray;//if this is not present it's not global $myNewArray
$myNewArray = array();
$data = array("a", "b", "c");
array_filter($data, function ($value) {
global $myNewArray;//this uses global
$myNewArray[] = $value;
});
print_r($myNewArray); // TEST OUTPUT
}
mainFunction();
Here is an example of you code without global $myNewArray
$var = "test";
function mainFunction($var) {
$myNewArray = array();
$data = array("a", "b", "c");
$myNewArray[] = array_filter($data, function ($value) {
return $value;
});
print_r($myNewArray); // TEST OUTPUT
}
mainFunction($var);
Answer to Update:
You can use array_reduce to achieve that
function mainFunction() {
global $var;
$myNewArray = array();
$data[] = array("id" => "1", "content" => "Hello");
$data[] = array("id" => "2", "content" => "World");
$myNewArray = array_reduce($data, function($accumulator, $item) {
if ($item['content'] === "World")
$accumulator[] = ['content' => $item['content']];
return $accumulator;
});
print_r($myNewArray); // TEST OUTPUT
}
mainFunction();
you can use this code..........
<?php
function test_odd($var)
{
return($var & 1);
}
$a1=array(1,3,2,3,4);
print_r(array_filter($a1,"test_odd"));
?>
Related
I'm working on project and I'm trying to refactor a json object By array of $keys
for example:
as input:
{
"message": "User Detail",
"code": 200,
"error": false,
"results": {
"user": {
"id": 2,
"name": "ali",
"country": {
"id": 50,
"name": "EGY"
}
},
"access_token": "=PQLkHJYIXB2uKbCq4sXIjD2GpBU2o"
}
}
as input: array of $keys
$kyes = [
'code',
'user'=>[
'id',
'country'=>['name']
]
]
expect to return:
{
"code": 200,
"user": {
"id": 2,
"country": {
"name": "egy",
}
}
}
code I have tried:
public function filter_multidimensional(array $array, array $keys){
$val = [];
foreach ($array as $key => $value) {
if (in_array($key,$keys)){
$val[$key] = $value;
}elseif (is_array($value)) {
$val[$key] = $this->filter_multidimensional($keys,$value);
}
}
return $val;
}
//-------
$this->filter_multidimensional($json,['code','user'=>['id','country'=>['name']]])
Unfortunately output:
update 1
the json input is not const, so my code must be adapt. and that's I'm trying to do.
Thanks for #404-not-found his code was amazing but missing a small thing
which is in this code
if (is_array($key)) {
$val[$objectKey] = filter_multidimensional($array, $key);
}
you still give the find function the base array which will return the first value it will find in the case of ['user'=>['id',"country"=>['id']]] and the solution for this will be passing the parent array of the object key
So the full code will be
<?php
function filter_multidimensional(array $array, array $keys) {
if (empty($keys) || empty($array)) {
return [];
}
$val = [];
// In the structure of $keys, both key and value are technically "keys".
// In the example `['code','user'=>['id']]`, 'code' and 'id' are both array *values*,
// while 'user' is a key.
//
// So, $objectKey is a search key which contains sub-keys, while $key is just a regular string.
foreach ($keys as $objectKey => $key) {
// If $key is an array, then recursively search, and save the results to the $objectKey string.
if (is_array($key)) {
$val[$objectKey] = filter_multidimensional(findTill($array,$objectKey), $key);
}
// If the desired key exists, then save the value.
else if (array_key_exists($key, $array)) {
$val[$key] = $array[$key];
}
// Otherwise, $key is a string, but it does not exist at this level of $array,
// so search the next-level up in $array, and merge the results into this level of $val.
else {
$val = array_merge($val, filter_multidimensional(nextLevel($array), [$key]));
}
}
return $val;
}
function findTill($array,$key) {
if (array_key_exists($key, $array)) {
return $array[$key];
}
return findTill(nextLevel($array),$key);
}
/**
* Create an array that contains only the array values from $array.
*
* Eg: If given ['a' => '1', 'b' => ['foo' => '2'], 'c' => ['hello' => 'world']],
* then return ['foo' => '2', 'hello' => 'world']
*
* #param array $array
* #return array
*/
function nextLevel(array $array) {
// Merge all of the array values together into one array
return array_reduce(
// Strip the keys
array_values(
// Filter to return only keys where the value is an array
array_filter(
$array,
function ($value) {
return is_array($value);
}
)
),
'array_merge',
[]
);
}
//-------
$obj = [
"message" => "User Detail",
"code" => 200,
"error" => false,
"results" => [
"user" => [
"id" => 2,
"name" => "ali",
"country" => [
"id" => 50,
"name" => "EGY",
],
],
"access_token" => "=PQLkHJYIXB2uKbCq4sXIjD2GpBU2o",
],
];
$result = filter_multidimensional($obj,['code','user'=>['id','country'=>['id','name']],"access_token"]);
I believe this method works. I flipped your logic, and instead of searching the $array to see if it's keys match any of those in $keys, I instead recursively searched $keys to see if $array had any matching values.
function filter_multidimensional(array $array, array $keys) {
if (empty($keys) || empty($array)) {
return [];
}
$val = [];
// In the structure of $keys, both key and value are technically "keys".
// In the example `['code','user'=>['id']]`, 'code' and 'id' are both array *values*,
// while 'user' is a key.
//
// So, $objectKey is a search key which contains sub-keys, while $key is just a regular string.
foreach ($keys as $objectKey => $key) {
// If $key is an array, then recursively search, and save the results to the $objectKey string.
if (is_array($key)) {
$val[$objectKey] = filter_multidimensional($array, $key);
}
// If the desired key exists, then save the value.
else if (array_key_exists($key, $array)) {
$val[$key] = $array[$key];
}
// Otherwise, $key is a string, but it does not exist at this level of $array,
// so search the next-level up in $array, and merge the results into this level of $val.
else {
$val = array_merge($val, filter_multidimensional(nextLevel($array), [$key]));
}
}
return $val;
}
/**
* Create an array that contains only the array values from $array.
*
* Eg: If given ['a' => '1', 'b' => ['foo' => '2'], 'c' => ['hello' => 'world']],
* then return ['foo' => '2', 'hello' => 'world']
*
* #param array $array
* #return array
*/
function nextLevel(array $array) {
// Merge all of the array values together into one array
return array_reduce(
// Strip the keys
array_values(
// Filter to return only keys where the value is an array
array_filter(
$array,
function ($value) {
return is_array($value);
}
)
),
'array_merge',
[]
);
}
//-------
$result = filter_multidimensional($obj,['code','user'=>['id','country'=>['name']]]);
You could use a combination of data_get and data_set to get what you want.
Change the input a bit so it's more consistent. Dot notation would be easiest.
$array = [
"message" => "User Detail",
"code" => 200,
"error" => false,
"results" => [
"user" => [
"id" => 2,
"name" => "ali",
"country" => [
"id" => 50,
"name" => "EGY",
],
],
"access_token" => "=PQLkHJYIXB2uKbCq4sXIjD2GpBU2o",
],
];
$keys = [
'code' => 'code',
'user.id' => 'results.user.id',
'user.country.name' => 'results.user.country.name',
];
$results = [];
foreach ($keys as $set_position => $get_position) {
data_set($results, $set_position, data_get($array, $get_position));
}
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 given the array:
array(
"firstName": null,
"lastName": null,
"category": [
"name": null,
"service": [
"foo" => [
"bar" => null
]
]
]
)
that needs to be transform into this:
array(
0 => "firstName",
1 => "lastName",
2 => "category",
"category" => [
0 => "name",
1 => "service",
"service" => [
0 => "foo",
"foo" => [
0 => "bar"
]
]
]
)
The loop should check if a value is an array and if so, it should add the key as a value (0 => category) to the root of array and then leave the key as it is (category => ...) and traverse the value again to build the tree as in example.
I am stuck with this and every time I try, I get wrong results. Is there someone who is array guru and knows how to simply do it?
The code so far:
private $array = [];
private function prepareFields(array $fields):array
{
foreach($fields as $key => $value)
{
if(is_array($value))
{
$this->array[] = $key;
$this->array[$key] = $this->prepareFields($value);
}
else
{
$this->array[] = $key;
}
}
return $this->array;
}
You could make use of array_reduce:
function prepareFields(array $array): array
{
return array_reduce(array_keys($array), function ($result, $key) use ($array) {
$result[] = $key;
if (is_array($array[$key])) {
$result[$key] = prepareFields($array[$key]);
}
return $result;
});
}
Demo: https://3v4l.org/3BfKD
You can do it with this, check the Demo
function array_format(&$array){
$temp_array = [];
foreach($array as $k=>$v){
$temp_array[] = $k;
if(is_array($v)){
array_format($v);
$temp_array[$k] = $v;
}
}
$array = $temp_array;
}
array_format($array);
print_r($array);
I have 3 objects and I want them to combine into 1 array. There are duplicate property names in objects, but I want them too (with renames property name). How can I do that?
$object1 = {
"id": "10",
"unit_number": "12565"
},
$object2 = {
"id": "20",
"full_name": "Lorem Ipsm"
},
$object3 = {
"id": "30",
"phone": "123456789"
}
I want the output like,
array = (
"id1" => "10",
"unit_number" => "12565",
"id2" => "20",
"full_name" => "Lorem Ipsm",
"id3" => "30",
"phone" => "123456789"
);
I have tried to assign them to one array like,
$arr = array();
$arr['obj1'] = $object1;
$arr['obj2'] = $object2;
$arr['obj3'] = $object3;
Now I thought of doing a foreach, but I am stuck. My actual object is too big. So there are many duplicates. Not just this one.
I think you can achieve this using below code,
$object1 = (object) ['id' => '10', "unit_number"=> "12565", "name" => 'Test name'];
$object2 = (object) ['id' => '20', "full_name"=> "Lorem Ipsm"];
$object3 = (object) ['id' => '30', "phone"=> "123456789", "name" => "test name 1"];
$array1 = (array) $object1;
$array2 = (array) $object2;
$array3 = (array) $object3;
function array_merge_dup_keys() {
$arrays = func_get_args();
$data = array();
foreach ($arrays as $a) {
foreach ($a as $k => $v) {
$key1 = check_key_exists($k,$data);
$data[$key1] = $v;
}
}
return $data;
}
function check_key_exists($key,$array,$loop_count=1)
{
if(array_key_exists ( $key , $array ))
{
$val = explode('_',$key);
$count = isset($val[1]) ? $val[1] : $loop_count;
$start_key = isset($val[0]) ? $val[0] : $key;
$key = $start_key.'_'.$loop_count;
$key = check_key_exists($key,$array,$count+1);
}
return $key;
}
$data = array_merge_dup_keys($array1 ,$array2,$array3);
The output ($data) of above code will be,
Array
(
[id] => 10
[unit_number] => 12565
[name] => Test name
[id_1] => 20
[full_name] => Lorem Ipsm
[id_2] => 30
[phone] => 123456789
[name_1] => test name 1
)
Maybe something like this? (untested, possible typos/syntax errors...)
// this looks like json, not PHP
// "object1": {
// "id": "10",
// "unit_number": "12565"
// },
// "object2": {
// "id": "20",
// "full_name": "Lorem Ipsm"
// },
// "object3": {
// "id": "30",
// "phone": "123456789"
// }
// here is a php array for that data
$objArray = array(
"object1" => array( "id"=>"10", "unit_number"=>"12565"),
"object2" => array( "id"=>"20", "full_name"=>"Lorem Ipsm"),
"object3" => array( "id"=>"30", "phone"=>"123456789")
);
$newArray = array();
foreach( $objArray as $key=>$value)
{
// the the id "append"
$idAppend = substr($key, strlen("object"));
foreach($value as $subkey=>$subvalue)
{
$newkey = $subkey;
if ( strcmp($subkey, "id") == 0 ) // it is the id string
{
$newkey = $subkey.$idAppend;
}
$newArray[$newkey] = $subvalue;
}
}
I'm trying to get this working:
I have an array that gets "deeper" every loop. I need to add a new array to the deepest "children" key there is.
while($row = mysql_fetch_assoc($res)) {
array_push($json["children"],
array(
"id" => "$x",
"name" => "Start",
"children" => array()
)
);
}
So, in a loop it would be:
array_push($json["children"] ...
array_push($json["children"][0]["children"] ...
array_push($json["children"][0]["children"][0]["children"] ...
... and so on. Any idea on how to get the key-selector dynamic like this?
$selector = "[children][0][children][0][children]";
array_push($json$selector);
$json = array();
$x = $json['children'];
while($row = mysql_fetch_assoc($res)) {
array_push($x,
array(
"id" => "$x",
"name" => "Start",
"children" => array()
)
);
$x = $x[0]['children'];
}
print_r( $json );
Hmmm - maybe better to assign by reference:
$children =& $json["children"];
while($row = mysql_fetch_assoc($res)) {
array_push($children,
array(
"id" => "$x",
"name" => "Start",
"children" => array()
)
);
$children =& $children[0]['children'];
}
$json = array();
$rows = range('a', 'c');
foreach (array_reverse($rows) as $x) {
$json = array('id' => $x, 'name' => 'start', 'children' => array($json));
}
print_r($json);
If you want to read an array via a string path, split the string in indices, and then you can do something like this to get the value
function f($arr, $indices) {
foreach ($indices as $key) {
if (!isset($arr[$key])) {
return null;
}
$arr = $arr[$key];
}
return $arr;
}