In PHP, I'd like to convert an array of objects like the following to a PHP array, using one of the properties as the associative array keys.
[
{ "id": 2, "name": "Suzy" },
{ "id": 3, "name": "Joe" },
{ "id": 4, "name": "Sara" }
]
like this...
[
2 => "Suzy",
3 => "Joe",
4 => "Sara"
]
I can't use array_map because you can't set the keys from my understanding, but I'm wondering if there's a one-liner way to do it without a foreach loop.
To be clear, I want to maintain the keys in the output array, not puts the original keys inside the new array values like they do here: PHP's array_map including keys
It appears by "object" you mean a JSON object. Given that, you can use array_column() to pull out a single column from each row, and then array_combine() to use one column for the keys and another for the values:
$json = '[
{ "id": 2, "name": "Suzy" },
{ "id": 3, "name": "Joe" },
{ "id": 4, "name": "Sara" }
]';
$array = json_decode($json, true);
$out = array_combine(array_column($array, 'id'), array_column($array, 'name'));
print_r($out);
Yields:
Array
(
[2] => Suzy
[3] => Joe
[4] => Sara
)
2 liners and has a foreach though.
<?php
$json = '[
{ "id": 2, "name": "Suzy" },
{ "id": 3, "name": "Joe" },
{ "id": 4, "name": "Sara" }
]';
$new_array = [];
foreach(json_decode($json,true) as $each_object) $new_array[$each_object['id']] = $each_object['name'];
print_r($new_array);
$json = '[
{ "id": 2, "name": "Suzy" },
{ "id": 3, "name": "Joe" },
{ "id": 4, "name": "Sara" }
]';
$array = json_decode($json, true);
$result = array_column($array, 'name', 'id');
Related
Let's say I have a Model which outputs 3 models that is this data:
[
{
"id": 1,
"data": [
{
"id": "coins",
"qty": 3
},
{
"id": "ruby",
"qty": 52
}
]
},
{
"id": 2,
"data": [
{
"id": "coins",
"qty": 140
}
]
},
{
"id": 3,
"data": [
{
"id": "coins",
"qty": 84
}
]
}
]
How would I, using Collections, sort this data by coins's qty and who has the most.
A nice clean way of doing this is with the "." operator.
$projects = Project::all()->load('tasks')->sortBy('data.qty');
Json is mainly used as a common format for sending data.
In Laravel you can convert a json object to a php array easily by using json_decode().
$phpArray = json_decode($json);
From here you can convert it to a collection to take advantage of laravels collection functions.
$laravelArray = collect($phpArray);
After this take a look at https://laravel.com/docs/5.8/collections to do sort/filter or do whatever you want to the array.
Or you can use pure php to solve this
$json is your json retrieved
$array = json_decode($json, true);
usort($array['data'], function($a, $b) {
return $a['qty'] <=> $b['qty'];
});
print_r($array);
See this example code
<?php
$json = '{
"Message": "Done.",
"Status": true,
"InnerData": [
{
"id": 66,
"name": "first",
"distance": 74
},
{
"id": 67,
"name": "second",
"distance": 153
},
{
"id": 68,
"name": "third",
"distance": 172
}
]
}';
$array = json_decode($json, true);
usort($array['InnerData'], function($a, $b) {
return $a['distance'] <=> $b['distance'];
});
print_r($array);
I hope it's helps you.
Thanks.
try this
$array = collect($array)->sortBy('data.qty')->reverse()->toArray();
I want to add values from one array to another that are both potentially multidimensional when they have the same ID. Hard to explain so I added example.
arr1 => new structure, without full data
arr2 => old structure, with full data
Examples if you'd like to help out:
// arr1 (the correct structure/order, without the full data)
[{
"id": "24",
"children": [{
"id": "21",
"children": [{
"id": "15"
}]
}]
}]
// arr2 (full data, not in any specific order, may be nested)
[{
"id": "24",
"name": " x",
"time": "0",
"status": "0"
}, {
"id": "21",
"children": [{
"id": "15",
"name": "x",
"time": "0",
"status": "0"
}],
"name": "x",
"time": "0",
"status": "0"
}]
// arr3 (desired output for this example)
[{
"id": "24",
"children": [{
"id": "21",
"children": [{
"id": "15",
"name": "x",
"time": "0",
"status": "0"
}],
"name": "x",
"time": "0",
"status": "0"
}],
"name": " x",
"time": "0",
"status": "0"
}]
I tried this:
function merge($arr1, $arr2) {
foreach($arr1 as $key => $value){
foreach($arr2 as $value2) {
if($value['id'] === $value2['id']){
$arr1[$key]['name'] = $value2['name'];
$arr1[$key]['time'] = $value2['time'];
$arr1[$key]['status'] = $value2['status'];
if (is_array($value)) {
$arr1[$key]['children'] = merge($arr1, $arr2);
}
}
}
}
return $arr1;
}
to combine them, but I can't figure out how to handle the nesting correctly. I have tried a lot of other things as well like using array_merge_recursive() but it doesn't work because I want to merge based on ID value. Any help on getting me on track would be awesome thanks.
Current output for example:
[{
"id": "24",
"children": [{
"id": "21",
"children": [{
"id": "15"
}]
}],
"name": " x",
"time": "0",
"status": "0"
}]
Desired output for example:
[{
"id": "24",
"children": [{
"id": "21",
"children": [{
"id": "15",
"name": "x",
"time": "0",
"status": "0"
}],
"name": "x",
"time": "0",
"status": "0"
}],
"name": " x",
"time": "0",
"status": "0"
}]
Edit: How about this?
$detailsClean = [];
foreach($array2 as $item) {
$detailsClean = removeDepth($item, $detailsClean);
}
foreach($array1 as $itemKey => $item) {
$array1[$itemKey] = addDetails($item, $detailsClean);
}
function removeDepth($array, $result) {
$id = $array['id'];
if (!empty($array['children'])) {
foreach($array['children'] as $child) {
$result = removeDepth($child, $result);
}
unset($array['children']);
}
$result[$id] = $array;
return $result;
}
function addDetails($array, $details) {
$id = $array['id'];
if (isset($details[$id])) {
$array = array_merge($array, $details[$id]);
if (!empty($array['children'])) {
foreach($array['children'] as $childKey => $child) {
$array['children'][$childKey] = addDetails($child, $details);
}
}
}
return $array;
}
$array1 is updated with the final result.
Here is an example with the data from your unedited post: http://phpio.net/s/7z09
EDIT - I think I understand the problem now
From the example you gave, I realised the problem - your old array has all the data, but not the parent-child relationships, so you want to populate the new array (with the correct relationships) with the data form the old array. The problem here is that the merge function would have to fetch data from an arbitrary generation in the old array to populate the new array. This could mean a lot of looping.
So I think the solution is first to loop through the old data and flatten it - just have an associative array where the key is the "id" value. Then go through the new array and populate it from the flattened, "lookup" array. Does that make sense? In any case, you'd have two functions:
$lookUp = array();
//recursive function to flatten $arr2 into $lookUp array.
function indexOriginal($arr, &$lookUp) {
foreach($arr as $value) {
$lookUp[$value["id"]] = $value;
if (isset($value['children'])) {
unset($lookUp[$value["id"]]['children']);
indexOriginal($value['children'], $lookUp);
}
}
}
indexOriginal($arr2, $lookUp);
Then you populate the new array:
function fillInNew($arr, $lookUp) {
$return = array();
foreach($arr as $value) {
$newEntry = $lookUp[$value["id"]];
if (isset($value['children'])) $newEntry['children'] = fillInNew($value['children'], $lookUp);
$return[] = $newEntry;
}
return $return;
}
$newArr = fillInNew($arr1, $lookUp);
And $newArr should be what you're looking for
OLD USELESS STUFF FROM BEFORE:
This part of your code is weird to me:
if (is_array($value)) {
$arr1[$key]['children'] = merge($arr1, $arr2);
}
Obviously I may be completely confused, but don't you just need to put this?
if (isset($value2['children'])) $arr1[$key]['children'] = array_merge($arr1[$key]['children'], $value2['children']);
EDIT: I added array_merge because I saw that the 'children' array in the incomplete version could also need merging.
EDIT 2: now I've noticed that children can have further children (makes sense, I guess), which is why you had the correct idea of using the function recursively. You just seem to have passed in the wrong arrays - you want to pass in $arr1[$key]['children'] (as the incomplete array) and $value2['children'] (as the complete array)
function merge($arr1, $arr2) {
foreach($arr1 as $key => $value){
foreach($arr2 as $value2) {
if($value['id'] === $value2['id']){
$arr1[$key]['name'] = $value2['name'];
$arr1[$key]['time'] = $value2['time'];
$arr1[$key]['status'] = $value2['status'];
if (isset($value2['children'])) $arr1[$key]['children'] = merge($arr1[$key]['children'], $value2['children']);
}
}
}
return $arr1;
}
Let's say I have the following array
[
{
"id": "16",
"name": "dog",
},
{
"id": "17",
"name": "cat",
},
{
"id": "18",
"name": "mouse",
}
]
I want to use a specific attribute, id as the key for the array. I could do this:
$someArray = [
["id" => "16", "name" => "dog"],
["id" => "17", "name" => "cat"],
["id" => "18", "name" => "mouse"]
];
$newArray = [];
foreach ($someArray as $currItem)
{
$newArray[$currItem["id"]] = $currItem;
}
Then I would have this (the desired outcome)
{
"16": {
"id": "16",
"name": "dog"
},
"17": {
"id": "17",
"name": "cat"
},
"18": {
"id": "18",
"name": "mouse"
}
}
My question is: is there a better way to do this? Do I really have to loop through every item just to redefine my array ever so slightly?
I seem to have found a solution using information from Rizier123's comment and this thread: PHP Change Array Keys
As far as I can tell array_column is only going to give me an array of ids, so I need to use it with array_combine and array_values. Please don't be afraid to post if you have a better answer
$someArray = [
["id" => "16", "name" => "a"],
["id" => "17", "name" => "b"],
["id" => "18", "name" => "c"]
];
$newArray = array_combine(array_column($someArray, "id"), $someArray);
You beat me to the answer but I might contribute a little anyway...
I'm not sure where your original array is coming from, but if you are decoding JSON, then you can provide a second param to force objects to be converted to associative arrays
$contents = trim(file_get_contents("/home/jaith/foo/foo.json"));
$arr = json_decode($contents, TRUE); // note the second parameter
$v = array_combine(array_column($arr, "id"), $arr);
var_dump($v);
EDIT:
If you can tolerate your output array having objects, this might also work:
$contents = trim(file_get_contents("/home/jaith/foo/foo.json"));
$arr = json_decode($contents);
$v = array_combine(
array_column(
array_map(
function($item) { return (array)$item; },
$arr
),
"id"
),
$arr
);
var_dump($v);
Keep in mind though that performance could become a concern for very very large arrays. This is doing a lot of array munging.
I am trying to create a custom controller for the WordPress JSON API plugin and so far everything is working except the JSON Data I have is not in the correct format.
This is my current JSON output:
{
"status": "ok",
"all_tags": {
"tag-1": {
"term_name": "Tag 1",
"category_details": {
"0": {
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
},
"2": {
"category_ID": 13,
"category_name": "category 2",
"category_count": 1
}
}
},
"tag-2": {
"term_name": "Tag 2",
"category_details": [
{
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
}
]
}
}
}
However, in order to parse the data I must have the json data in a specific format. The correct format should be like this:
{
"status": "ok",
"all_tags": [
{
"id": 1,
"term_name": "Tag 1",
"category_details": [
{
"id": 2,
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
},
{
"id": 3,
"category_ID": 13,
"category_name": "category 2",
"category_count": 1
}
]
},
{
"id": 2,
"term_name": "Tag 2",
"category_details": [
{
"id": 2,
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
}
]
}
]
}
This is how I am creating the array for the json:
<?php
...
$cats_all = array(); // the array
if (!isset($cats_all[$custom_term->slug])) {
// create the array
$cats_all[$custom_term->slug] = array(
'term_name' => $custom_term->name,
'category_details' => array(
array(
'category_ID' => $categories[0]->term_id,
'category_name' => $categories[0]->name,
'category_count' => $mycats[0]->category_count
)
)
);
} else {
$cats_all[$custom_term->slug]['category_details'][] = array(
'category_ID' => $categories[0]->term_id,
'category_name' => $categories[0]->name,
'category_count' => $mycats[0]->category_count
);
}
...
// remove duplicates
$input = $this->super_unique( $cats_all );
// return the array for json output by the plugin
return array(
'all_tags' => $input,
);
Any help will be greatly appreciated. Also the entire controller can be viewed here.
There are two things you need to achieve:
The all_tags value must be a sequential array, not an associative one. This you can achieve by taking the array_values at the last statement:
return array(
'all_tags' => array_values($input)
);
The category_details values must be sequential arrays, not associative ones. This one is more tricky, as you actually do create them as sequential arrays, but the function super_unique will sometimes turn them into associative arrays, when it eliminates at least one duplicate. I suggest this fix to the function super_unique by adding two statements, around this one:
$result = array_map( 'unserialize', array_unique( array_map( 'serialize', $array ) ) );
To get this:
$is_seq = end(array_keys($array)) == count($array)-1;
$result = array_map( 'unserialize', array_unique( array_map( 'serialize', $array ) ) );
if ($is_seq) $result = array_values($result);
If you get an error on end, then you can use this for that line instead:
end($array); $is_seq = key($array) == count($array)-1;
The $is_seq variable checks that $array is sequential, and if so, calls array_values after the removal of the duplicates, which always returns a sequential array.
I think that the problem here is this:
"0": {
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
},
"2": {
"category_ID": 13,
"category_name": "category 2",
"category_count": 1
}
if you want an array to be a json encoded array, indexes must be numeric and correlatives (0,1,2,3...)
Probably, after using your $this->super_unique( $cats_all ), or inside this function, you should call array_values on every array that has been reindexed; it reset the values of the array to 0, 1, ... etc... and when encoded, it will be an array isntead of an object.
Generally speaking, it´s a good practice to use $array = array_values($array) after using array_filter(...) to correctly reindex the array, elsewhere, you can get indexes like 0,2,7... etc...
Let me know if you need more details
How would I merge both JSON arrays and then sort the value of ID so that the result displays the highest number to the lowest number?
For example, my desired output from the script below would be:
1 - Jimbo
2 - Bob
6 - Luke
12 - Chris
16 - Jonas
36 - Sam
Here's my JSON arrays:
$json1 ='
{
"error": "trueee",
"info": {
"collections": [{
"ID": "1"
"Name": "Jimbo"
}, {
"ID": "36"
"Name": "Sam"
}, {
"ID": "2",
"Name": "Bob"
}]
}
}
';
$json2 ='
{
"error": "trueee",
"info": {
"collections": [{
"ID": "12"
"Name": "Chris"
}, {
"ID": "6"
"Name": "Luke"
}, {
"ID": "16"
"Name": "Jonas"
}]
}
}
';
You need to merge the arrays from the json string. First json decode for getting the arr with associative array than get the columns of ID using array_column, after that you need to merge the two array and finally sort them.
Online Check and a Long Conversation
$json1 ='
{
"error": "trueee",
"info": {
"collections": [{
"ID": "1"
}, {
"ID": "36"
}, {
"ID": "2"
}]
}
}
';
$json2 ='
{
"error": "trueee",
"info": {
"collections": [{
"ID": "12"
}, {
"ID": "6"
}, {
"ID": "16"
}]
}
}
';
$arr1 = json_decode($json1, true);
$arr2 = json_decode($json2, true);
$arr1 = array_column($arr1['info']['collections'], "ID");
$arr2 = array_column($arr2['info']['collections'], "ID");
$arr = array_merge($arr1, $arr2);
sort($arr);
echo '<pre>';
print_r($arr);
Result:
Array
(
[0] => 1
[1] => 2
[2] => 6
[3] => 12
[4] => 16
[5] => 36
)