I have a json data. Now I want to reform it. In my json data there is a property like person_on_zone I want to make a property named person_info and store them which person under this area.
Here is my data any code. Thanks in advance
My json data
$val = [
{
'city':'xx',
'zone':'yy',
'person_on_zone':'p1'
},
{
'city':'xx',
'zone':'yy',
'person_on_zone':'p2'
},
{
'city':'xx',
'zone':'yy',
'person_on_zone':'p3'
},
{
'city':'xx',
'zone':'ww',
'person_on_zone':'p1'
},
]
My expectation is
[
{
'city':'xx',
'zone':'yy',
'person_info':{
'person_on_zone':'p1',
'person_on_zone':'p2',
'person_on_zone':'p3',
}
},
{
'city':'xx',
'zone':'ww',
'person_info':{
'person_on_zone':'p1'
}
},
]
Here I tried
foreach ($val as $v) {
$new_array['city'] = $v['city'];
$new_array['zone'] = $v['zone'];
foreach ($val as $v2) {
$new_array['person_info'] = $v['person_on_zone'];
}
}
json_encode($new_array);
Try this, use $map to store key city-zone, then transform to array to get the result
<?php
$val = [
[
'city' => 'xx',
'zone' => 'yy',
'person_on_zone' => 'p1'
],
[
'city' => 'xx',
'zone' => 'yy',
'person_on_zone' => 'p2'
],
[
'city' => 'xx',
'zone' => 'yy',
'person_on_zone' => 'p3'
],
[
'city' => 'xx',
'zone' => 'ww',
'person_on_zone' => 'p1'
]
];
$map = [];
foreach ($val as $v) {
$key = $v['city'] . $v['zone'];
if (!isset($map[$key])) {
$map[$key] = [
'city' => $v['city'],
'zone' => $v['zone'],
'person_info' => []
];
}
$map[$key]['person_info'][] = $v['person_on_zone'];
}
print_r(array_values($map));
Correct Code
foreach ($val as $v){
$new_array['city'] = $v['city'];
$new_array['zone'] = $v['zone'];
foreach ($val as $v2){
$new_array['person_info'] = $v2['person_on_zone'];
}
}
json_encode($new_array);
try this:
first decode json array then use foreach loop with key
$val = json_decode($val);
foreach ($val as $v){
$new_array['city'] = $v->city;
$new_array['zone'] = $v->zone;
foreach ($val as $key=>$v2){
$new_array[$key]['person_info'] = $v->person_on_zone;
}
}
print_r($new_array);
I think you make a mistake around composing the json. I tried with your code and i found following is the correct way to do.. Remember single quote(') is not valid in json string that is being used to define key value pair in php
// I think your code has json string and i convert it into array of objects(stdClass)
// and if your code has array then keep you code intact but need to change the notation of
// objects to associative array.
// first thing first decode json string
$values = json_decode('[
{ "city":"xx",
"zone":"yy",
"person_on_zone":"p1"
},
{
"city":"xx",
"zone":"yy",
"person_on_zone":"p2"
},
{
"city":"xx",
"zone":"yy",
"person_on_zone":"p3"
},
{
"city":"xx",
"zone":"ww",
"person_on_zone":"p1"
}]');
// Now declare new values as array
$new_values = [];
// walk through each object
foreach ($values as $v){
// $v becomes an object of stdClass here
// $data is a temporary array
$data['city'] = $v->city;
$data['zone'] = $v->zone;
$data['person_info'] = [];
foreach ($values as $v2){
if(!in_array($data['person_info'],['person_on_zone' => $v2->person_on_zone]))
{
// compare if zones are same or not
if($v->zone == $v2->zone)
{
// push to the array instead of assiging
array_push($data['person_info'], ['person_on_zone' => $v2->person_on_zone]);
}
}
}
// make array unique
$data['person_info'] = array_map("unserialize", array_unique(array_map("serialize", $data['person_info'])));
array_push($new_values, $data);
}
// make array unique in mutidimensional array
$new_values = array_map("unserialize", array_unique(array_map("serialize", $new_values)));
echo '<pre>';
print_r($new_values);
echo '</pre>';
One simple foreach would do,
$result = [];
foreach ($arr as $key => $value) {
//created unique combination of city and zone as key
$result[$value['city'] . $value['zone']]['city'] = $value['city'];
$result[$value['city'] . $value['zone']]['zone'] = $value['zone'];
$result[$value['city'] . $value['zone']]['person_info'][] = ['person_on_zone' => $value['person_on_zone']];
}
echo json_encode(array_values($result));die;
array_values — Return all the values of an array
Working demo.
Related
I have a multidimensional array that can have any depth. What im trying to do is to filter the whole path based on dynamic keys and create a new array of it.
Example of the array
$originalArray = [
"title" => "BACKPACK MULTICOLOUR",
"description" => "description here",
"images" => [
[
"id" => 12323123123,
"width" => 635,
"height" => 560,
"src" => "https://example.com",
"variant_ids": [32694976315473, 32863017926737],
],
[
"id" => 4365656656565,
"width" => 635,
"height" => 560,
"src" => "https://example.com",
"variant_ids": [32694976315473, 32863017926737],
]
],
"price" => [
"normal" => 11.00,
"discount" => [
"gold_members" => 9.00,
"silver_members" => 10.00,
"bronze_members" => null
]
]
];
Example how the output should look like with the key "title, width, height, gold_members" filtered out. Only keys from the end of the array tree should be valid, so nothing must happen when images is in the filter
$newArray = [
"title" => "BACKPACK MULTICOLOUR",
"images" => [
[
"width" => 635,
"height" => 560,
],
[
"width" => 635,
"height" => 560,
]
],
"price" => [
"discount" => [
"gold_members" => 9.00,
]
]
];
I guess that i should create a function that loop through each element and when it is an associative array, it should call itself again
Because the filtered paths are unknown i cannot make a hardcoded setter like this:
$newArray["images"][0]["width"] = 635
The following filter will be an example but it should basically be dynamic
example what i have now:
$newArray = handleArray($originalArray);
handleArray($array)
{
$filter = ["title", "width", "height", "gold_members"];
foreach ($array as $key => $value) {
if (is_array($value)) {
$this->handleArray($value);
} else {
if (in_array($key, $filter)) {
// put this full path in the new array
}
}
}
}
[Solved] Update:
I solved my problem thanks to #trincot
I used his code and added an extra check to add an array with multiple values to the new array
My code to solve the issue:
<?php
function isListOfValues($array) {
$listOfArrays = [];
foreach ($array as $key => $value) {
$listOfArrays[] = ! is_array($value) && is_int($key);
}
return array_sum($listOfArrays) === count($listOfArrays);
}
function filterKeysRecursive(&$arr, &$keep) {
$result = [];
foreach ($arr as $key => $value) {
if (is_array($value) && ! isListOfValues($value)) {
$value = filterKeysRecursive($value, $keep);
if (count($value)) {
$result[$key] = $value;
}
} else if (array_key_exists($key, $keep)) {
$result[$key] = $value;
}
}
return $result;
}
$keep = array_flip(["title", "width", "height", "gold_members"]);
$result = filterKeysRecursive($originalArray, $keep);
You could use a recursive function, with following logic:
base case: the value associated with a key is not an array (it is a "leaf"). In that case the new object will have that key/value only when the key is in the list of desired keys.
recursive case: the value associated with a key is an array. Apply recursion to that value. Only add the key when the returned result is not an empty array. In that case associate the filtered value to the key in the result object.
To speed up the look up in the list of keys, it is better to flip that list into an associative array.
Here is the implementation:
function filter_keys_recursive(&$arr, &$keep) {
foreach ($arr as $key => $value) {
if (is_array($value)) {
$value = filter_keys_recursive($value, $keep);
if (count($value)) $result[$key] = $value;
} else if (array_key_exists($key, $keep)) {
$result[$key] = $value;
}
}
return $result;
}
$originalArray = ["title" => "BACKPACK MULTICOLOUR","description" => "description here","images" => [["id" => 12323123123,"width" => 635,"height" => 560,"src" => "https://example.com"],["id" => 4365656656565,"width" => 635,"height" => 560,"src" => "https://example.com"]],"price" => ["normal" => 11.00,"discount" => ["gold_members" => 9.00,"silver_members" => 10.00,"bronze_members" => null]]];
$keep = array_flip(["title", "width", "height", "gold_members"]);
$result = filter_keys_recursive($originalArray, $keep);
My proposition to you is to write a custom function to transform structure from one schema to another:
function transform(array $originalArray): array {
array_walk($originalArray['images'], function (&$a, $k) {
unset($a['id']); unset($a['src']);
});
unset($originalArray['description']);
unset($originalArray['price']['normal']);
unset($originalArray['price']['discount']['silver_members']);
unset($originalArray['price']['discount']['bronze_members']);
return $originalArray;
}
var_dump(transform($originalArray));
If you are familiar with OOP I suggest you to look at how DTO works in API Platform for example and inject this idea into your code by creating custom DataTransformers where you specify which kind of structers you want to support with transformer and a method where you transform one structure to another.
Iterate over the array recursively on each key and subarray.
If the current key in the foreach is a required key in the result then:
If the value is not an array, simply assign the value
If the value is an array, iterate further down over value recursively just in case if there is any other filtering of the subarray keys that needs to be done.
If the current key in the foreach is NOT a required key in the result then:
Iterate over value recursively if it's an array in itself. This is required because there could be one of the filter keys deep down which we would need. Get the result and only include it in the current subresult if it's result is not an empty array. Else, we can skip it safely as there are no required keys down that line.
Snippet:
<?php
function filterKeys($array, $filter_keys) {
$sub_result = [];
foreach ($array as $key => $value) {
if(in_array($key, $filter_keys)){// if $key itself is present in $filter_keys
if(!is_array($value)) $sub_result[$key] = $value;
else{
$temp = filterKeys($value, $filter_keys);
$sub_result[$key] = count($temp) > 0 ? $temp : $value;
}
}else if(is_array($value)){// if $key is not present in $filter_keys - iterate over the remaining subarray for that key
$temp = filterKeys($value, $filter_keys);
if(count($temp) > 0) $sub_result[$key] = $temp;
}
}
return $sub_result;
}
$result = filterKeys($originalArray, ["title", "width", "height", "gold_members"]);
print_r($result);
Online Demo
Try this way.
$expectedKeys = ['title','images','width','height','price','gold_members'];
function removeUnexpectedKeys ($originalArray,$expectedKeys)
{
foreach ($originalArray as $key=>$value) {
if(is_array($value)) {
$originalArray[$key] = removeUnexpectedKeys($value,$expectedKeys);
if(!is_array($originalArray[$key]) or count($originalArray[$key]) == 0) {
unset($originalArray[$key]);
}
} else {
if (!in_array($key,$expectedKeys)){
unset($originalArray[$key]);
}
}
}
return $originalArray;
}
$newArray = removeUnexpectedKeys ($originalArray,$expectedKeys);
print_r($newArray);
check this on editor,
https://www.online-ide.com/vFN69waXMf
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 that looks like the following:
[
'applicant' => [
'user' => [
'username' => true,
'password' => true,
'data' => [
'value' => true,
'anotherValue' => true
]
]
]
]
What I want to be able to do is convert that array into an array that looks like:
[
'applicant.user.username',
'applicant.user.password',
'applicant.user.data.value',
'applicant.user.data.anotherValue'
]
Basically, I need to somehow loop through the nested array and every time a leaf node is reached, save the entire path to that node as a dot separated string.
Only keys with true as a value are leaf nodes, every other node will always be an array. How would I go about accomplishing this?
edit
This is what I have tried so far, but doesnt give the intended results:
$tree = $this->getTree(); // Returns the above nested array
$crumbs = [];
$recurse = function ($tree, &$currentTree = []) use (&$recurse, &$crumbs)
{
foreach ($tree as $branch => $value)
{
if (is_array($value))
{
$currentTree[] = $branch;
$recurse($value, $currentTree);
}
else
{
$crumbs[] = implode('.', $currentTree);
}
}
};
$recurse($tree);
This function does what you want:
function flattenArray($arr) {
$output = [];
foreach ($arr as $key => $value) {
if (is_array($value)) {
foreach(flattenArray($value) as $flattenKey => $flattenValue) {
$output["${key}.${flattenKey}"] = $flattenValue;
}
} else {
$output[$key] = $value;
}
}
return $output;
}
You can see it running here.
I have a multidimensional array containing comma-separated strings like this
[
[
"users" => [
'email' => 'test#yahoo.com ,testuser#yahoo.com',
'username' => 'test,testuser',
'description' => 'description1,description2'
]
]
]
I want to access the users subarray data, explode on delimiters, and create a new associative array of indexed arrays.
Desired result:
$User = array(
'email' => array(
'test#yahoo.com',
'testuser#yahoo.com'
),
'username' => array(
'test',
'testuser'
),
'description' => array(
'description1',
'description2'
)
);
For only one index:
$arrayTwoD = array();
foreach ($valueMult[0]['User'] as $key => $value) {
$arrayTwoD[$key] = array_push(explode(',', $value));
}
If you have multiple indexes in $multArray:
$arrayTwoD = array();
foreach ($multArray as $keyMult => $valueMult) {
foreach ($valueMult['User'] as $key => $value) {
$arrayTwoD[$keyMult][$key] = array_push(explode(',', $value));
}
}
or
$arrayTwoD = array();
foreach ($multArray as $array) {
foreach ($array['User'] as $key => $value) {
$arrayTwoD[$key] = array_push(explode(',', $value));
}
}
try this
$array = array(...); // your array data
$formedArray = array();
foreach ( $array as $arr )
{
foreach ( $arr['user'] as $key => $value )
{
$formedArray[$key] = array_push(explode(",",$value));
}
}
echo "<pre>";
print_r($formedArray);
echo "</pre>";
It's a little bit repetitive, I know, but you can do like this as well:
foreach($array as $users) {
foreach($users as &$value) { // &value is assigned by reference
$users['users']["email"] = explode(",", $value['email']);
$users['users']["username"] = explode(",", $value['username']);
$users['users']["description"] = explode(",", $value['description']);
}
}
But after that, you need to use $value. Refer to the official PHP manual documentation to know more about what the & symbol does here.
Demo
Using array_map() can be used to access the subset data (without declaring any new variables in the global scope) and make iterate calls of preg_split() to separate the delimited values into subarrays.
Code: (Demo)
var_export(
array_map(
fn($csv) => preg_split('/ ?,/', $csv),
$array[0]['users']
)
);
In an array such as the one below, how could I rename "fee_id" to "id"?
Array
(
[0] => Array
(
[fee_id] => 15
[fee_amount] => 308.5
[year] => 2009
)
[1] => Array
(
[fee_id] => 14
[fee_amount] => 308.5
[year] => 2009
)
)
foreach ( $array as $k=>$v )
{
$array[$k] ['id'] = $array[$k] ['fee_id'];
unset($array[$k]['fee_id']);
}
This should work
You could use array_map() to do it.
$myarray = array_map(function($tag) {
return array(
'id' => $tag['fee_id'],
'fee_amount' => $tag['fee_amount'],
'year' => $tag['year']
); }, $myarray);
$arrayNum = count($theArray);
for( $i = 0 ; $i < $arrayNum ; $i++ )
{
$fee_id_value = $theArray[$i]['fee_id'];
unset($theArray[$i]['fee_id']);
$theArray[$i]['id'] = $fee_id_value;
}
This should work.
Copy the current 'fee_id' value to a new key named 'id' and unset the previous key?
foreach ($array as $arr)
{
$arr['id'] = $arr['fee_id'];
unset($arr['fee_id']);
}
There is no function builtin doing such thin afaik.
This is the working solution, i tested it.
foreach ($myArray as &$arr) {
$arr['id'] = $arr['fee_id'];
unset($arr['fee_id']);
}
The snippet below will rename an associative array key while preserving order (sometimes... we must). You can substitute the new key's $value if you need to wholly replace an item.
$old_key = "key_to_replace";
$new_key = "my_new_key";
$intermediate_array = array();
while (list($key, $value) = each($original_array)) {
if ($key == $old_key) {
$intermediate_array[$new_key] = $value;
}
else {
$intermediate_array[$key] = $value;
}
}
$original_array = $intermediate_array;
Converted 0->feild0, 1->field1,2->field2....
This is just one example in which i get comma separated value in string and convert it into multidimensional array and then using foreach loop i changed key value of array
<?php
$str = "abc,def,ghi,jkl,mno,pqr,stu
abc,def,ghi,jkl,mno,pqr,stu
abc,def,ghi,jkl,mno,pqr,stu
abc,def,ghi,jkl,mno,pqr,stu;
echo '<pre>';
$arr1 = explode("\n", $str); // this will create multidimensional array from upper string
//print_r($arr1);
foreach ($arr1 as $key => $value) {
$arr2[] = explode(",", $value);
foreach ($arr2 as $key1 => $value1) {
$i =0;
foreach ($value1 as $key2 => $value2) {
$key3 = 'field'.$i;
$i++;
$value1[$key3] = $value2;
unset($value1[$key2]);
}
}
$arr3[] = $value1;
}
print_r($arr3);
?>
I wrote a function to do it using objects or arrays (single or multidimensional) see at https://github.com/joaorito/php_RenameKeys.
Bellow is a simple example, you can use a json feature combine with replace to do it.
// Your original array (single or multi)
$original = array(
'DataHora' => date('YmdHis'),
'Produto' => 'Produto 1',
'Preco' => 10.00,
'Quant' => 2);
// Your map of key to change
$map = array(
'DataHora' => 'Date',
'Produto' => 'Product',
'Preco' => 'Price',
'Quant' => 'Amount');
$temp_array = json_encode($original);
foreach ($map AS $k=>$v) {
$temp_array = str_ireplace('"'.$k.'":','"'.$v.'":', $temp);
}
$new_array = json_decode($temp, $array);
Multidimentional array key can be changed dynamically by following function:
function change_key(array $arr, $keySetOrCallBack = [])
{
$newArr = [];
foreach ($arr as $k => $v) {
if (is_callable($keySetOrCallBack)) {
$key = call_user_func_array($keySetOrCallBack, [$k, $v]);
} else {
$key = $keySetOrCallBack[$k] ?? $k;
}
$newArr[$key] = is_array($v) ? array_change_key($v, $keySetOrCallBack) : $v;
}
return $newArr;
}
Sample Example:
$sampleArray = [
'hello' => 'world',
'nested' => ['hello' => 'John']
];
//Change by difined key set
$outputArray = change_key($sampleArray, ['hello' => 'hi']);
//Output Array: ['hi' => 'world', 'nested' => ['hi' => 'John']];
//Change by callback
$outputArray = change_key($sampleArray, function($key, $value) {
return ucwords(key);
});
//Output Array: ['Hello' => 'world', 'Nested' => ['Hello' => 'John']];
I have been trying to solve this issue for a couple hours using recursive functions, but finally I realized that we don't need recursion at all. Below is my approach.
$search = array('key1','key2','key3');
$replace = array('newkey1','newkey2','newkey3');
$resArray = str_replace($search,$replace,json_encode($array));
$res = json_decode($resArray);
On this way we can avoid loop and recursion.
Hope It helps.