I am returning some data from DB using Eloquent and putting in in object of arrays. My response object to browser is displayed in this format:
// response()->json($response, 200);
[{
"id": 1,
"name": "car",
"make": ["bmw", "ford"]
"order": 1
},
{
"id": 2,
"name": "bike",
"make": ["aprilia"]
"order": 2
},
{
"id": 3,
"name": "boat",
"make": []
"order": 3
},
(...)
]
Before returning it though, I wanted to filter it on server side. So I only return objects which do hold value in the "make" array.
So I am running this loop:
foreach ($response as $key => $transport) {
if (count($response[$key]['make']) == 0) {
unset($response[$key]);
};
}
What php does is it converts the array to object and also adds keys to each inner object. So now my $response looks like:
// response()->json($response, 200);
{ // notice here it has changed from array to object
"0": { // notice here it has added key "0"
"id": 1,
"name": "car",
"make": ["bmw", "ford"]
"order": 1
},
"1" : { // notice here it has added key "1"
"id": 2,
"name": "bike",
"make": ["aprilia"]
"order": 2
},
(...)
}
First of all - why?
And second question - how to prevent/go back to array of objects response?
When you unset a value from an indexed array in PHP, the existing indexes remain. Using a simple example with a small range of integers to illustrate, the following code unsets all odd integers from an array:
$numbers = range(1, 5); // [1, 2, 3, 4, 5];
foreach ($numbers as $index => $number) {
if ($number % 2 !== 0) {
unset($numbers[$index]);
}
}
print_r($numbers);
Which yields:
Array
(
[1] => 2
[3] => 4
)
Note that the odd elements are removed, but the indexes of the existing elements are retained. Because of these gaps, the array isn't sequentially indexed at this point.
The consequence of this is, when you json_encode() this array, it assumes that these non-sequential indexes (let's just call them keys at this point) are to be retained, so it creates an object literal with keys instead. These keys are strings that just happen to be integers:
echo json_encode($numbers); // {"1":2,"3":4}
Using array_values($numbers) will reset your array's indexes:
$reindexed = array_values($numbers);
echo json_encode($reindexed); // [2, 4]
Note: I mentioned in a comment that you can cast to an array using (array)—this is actually not correct as it will retain the non sequential indexes.
Hope this helps!
Related
This question already has answers here:
json_encode PHP array as JSON array not JSON object
(4 answers)
Closed 3 years ago.
I have removed the duplicate items for the following array, by writing:
$outcome['id'] = array_unique($outcome['id'], SORT_REGULAR);
but I'm getting undesirable JSON output
"id": {
"0": {
"id": 947,
"label": "ABCD"
},
"1": {
"id": 2175,
"label": "EFGH"
},
"2": {
"id": 15,
"label": "IJKL"
}
}
instead of the below , which is the desirable JSON output :
"id": [
{
"id": 947,
"label": "ABCD"
},
{
"id": 2175,
"label": "EFGH"
},
{
"id": 15,
"label": "IJKL"
}
]
While debugging on PHPStorm, the result shown was in array format, but on Postman, the result is being transformed to object!
array_unique preserves keys while removing items. This means it'll remove some array entries but keep indexes of remaining items intact, resulting in non-continuous numerical indices (gaps). An array with gaps in their numerical indices or an array with non-numerical indices counts as an associative array which in JSON becomes a JSON object. You can reset the indices by using array_values like e.g.
$outcome['id'] = array_values(array_unique($outcome['id'], SORT_REGULAR));
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');
I have this piece of code to get a difference between two arrays of objects:
$diff = array_udiff($a, $b,
function($obj_a, $obj_b) {
return $obj_a->id - $obj_b->id;
}
);
$a is
[
{
"id": "7",
"attribute": "instagram"
},
{
"id": "8",
"attribute": "snapchat"
},
{
"id": "9",
"attribute": "facebook"
}
]
$b is
[
{
"id": "7",
"attribute": "instagram",
"value": "somevalue"
}
]
$diff should return array of objects
[
{
"id": "8",
"attribute": "snapchat"
},
{
"id": "9",
"attribute": "facebook"
}
]
And it does, but only when $b is an empty array. The result is then correct (I get an array of objects, the $a, because $b is empty).
But when at least one object is present in the $b, the $diff is returning to me the following which I don't want.
{
"1": {
"id": "8",
"attribute": "snapchat"
},
"2": {
"id": "9",
"attribute": "facebook"
}
}
How do I get an array of objects in the result instead of the above? And why there is a difference between the two results? Thanks.
You just need to use $diff = array_values($diff); before you json_encode so that the indexes will start at zero.
When you do the array_udiff, the keys of $a are preserved, so $diff has keys starting at 1 instead of 0. In order for json_encode to encode an array as a JSON array instead of a JSON object, the keys must be sequential and start at zero. array_values will reindex the array so that this condition will be met.
Just to be clear, even if other items were removed from the array instead of the first one, so that the indexes still started with zero, you would still need to reindex $diff because of the missing keys.
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
This question already has answers here:
PHP Array to JSON Array using json_encode(); [duplicate]
(4 answers)
Closed 8 years ago.
Below is my JSON Result when I do not replace the order of channels as per timings:
"channels": [
{
"id": "1",
"name": "LBC دراما "
},
{
"id": "2",
"name": "KTV Arabe"
},
{
"id": "3",
"name": "KTV Plus"
}
]
Now, when I replace the array keys with values returned by array_search function, it brings the key with array response which is problem for me:
"channels": {
"0": {
"id": "2",
"name": "KTV Arabe"
},
"1": {
"id": "1",
"name": "LBC دراما "
},
"3": {
"id": "3",
"name": "KTV Plus"
}
}
code:
$newChannelsArr['channels'][array_search($channelsArr['channels'][$a]['id'], $data)] = ($channelsArr['channels'][$a]);
How can I overcome from keys getting appended in my json array?
My Code Snippet:
$data values:
Array
(
[0] => 2
[1] => 1
[3] => 3
)
$newChannelsArr = array();
if(isset($data)){
for($a = 0; $a < count($data); $a++){
$kv = array_search($channelsArr['channels'][$a]['id'], $data);
$newChannelsArr['channels'][(int)$kv] = ($channelsArr['channels'][$a]);
}
}
Solution:
Solution
ksort($newChannelsArr['channels']); // sort an array
$arr = array_map('array_values', $arr); // regenerate their keys
It will be a quick patch.
No, because JSON array does not have keys. That's why when you added it, in second response you now got object, not array you had before and what you wanted to be your array keys become properties of object. You cannot access object as array. You must now access it as object:
$foo->property...