array_udiff returns different results - php

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.

Related

Laravel Sort by data in JSON

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();

Combine Two Multidimensional Arrays in PHP With The Same ID

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;
}

how can I avoid json_encode adding key values to my array objects? [duplicate]

I'm not sure why this is happening, but I seem to run into this problem often. Here is my original JSON for a shopping cart:
{
"cartitems": [
{
"Product_ID": "1",
"quantity": "1",
"cartid": 1
},
{
"Product_ID": "5",
"quantity": "1",
"cartid": 4
},
{
"Product_ID": "5",
"quantity": "1",
"cartid": 6
},
{
"Product_ID": "5",
"quantity": "1",
"cartid": 7
}
]
}
This JSON data is stored to the $_SESSION variable, $_SESSION['cart_items']
This code is used to remove an item:
$cartid = $_POST['varA'];
/* Remove the item */
foreach ($_SESSION['cart_items']['cartitems'] as $key => $product) {
if ($product['cartid'] == $cartid) {
unset($_SESSION['cart_items']['cartitems'][$key]);
}
}
echo json_encode($_SESSION['cart_items']);
When the item with cartid = 7 is removed, the result is this when it is endoded:
{
"cartitems": {
"0": {
"Product_ID": "1",
"quantity": "1",
"cartid": 1
},
"1": {
"Product_ID": "5",
"quantity": "1",
"cartid": 4
},
"2": {
"Product_ID": "5",
"quantity": "1",
"cartid": 6
}
}
}
It adds keys! This only occurs when there are more than 3 items, which baffles me. Is there any way I can re-write my code so that it prevents theses keys from being created?
In PHP, there are only arrays, which are used for both associative and numerically indexed maps/lists/arrays. Javascript/JSON has two distinct concepts: numerically indexed arrays ([...]) and object maps ({ foo : ... }). For PHP's json_encode to decide which to use when encoding an array, there's some logic behind the scenes. Generally, if the array keys are contiguous and all numerical, the array is encoded to a JSON array ([...]). If there's even one key out of order or a non-numeric key, a JSON object is used instead.
Why your array manipulation in particular triggers an object, I don't know. To avoid this though, you can reset your array keys to make sure they're numerically, contiguously indexed:
$_SESSION['cart_items']['cartitems'] = array_values($_SESSION['cart_items']['cartitems']);
Try this, worked for me.
Transfer the array to a new array with auto keys:
/* Remove the item */
foreach ($_SESSION['cart_items']['cartitems'] as $key => $product) {
if ($product['cartid'] == $cartid) {
unset($_SESSION['cart_items']['cartitems'][$key]);
}
}
$var=array();
foreach($_SESSION['cart_items']['cartitems'] as $key => $product) {
$var['cart_items']['cartitems'][] = $product;
}
echo json_encode($var['cart_items']);

Why can't I get php to write normal arrays to json? [duplicate]

I'm not sure why this is happening, but I seem to run into this problem often. Here is my original JSON for a shopping cart:
{
"cartitems": [
{
"Product_ID": "1",
"quantity": "1",
"cartid": 1
},
{
"Product_ID": "5",
"quantity": "1",
"cartid": 4
},
{
"Product_ID": "5",
"quantity": "1",
"cartid": 6
},
{
"Product_ID": "5",
"quantity": "1",
"cartid": 7
}
]
}
This JSON data is stored to the $_SESSION variable, $_SESSION['cart_items']
This code is used to remove an item:
$cartid = $_POST['varA'];
/* Remove the item */
foreach ($_SESSION['cart_items']['cartitems'] as $key => $product) {
if ($product['cartid'] == $cartid) {
unset($_SESSION['cart_items']['cartitems'][$key]);
}
}
echo json_encode($_SESSION['cart_items']);
When the item with cartid = 7 is removed, the result is this when it is endoded:
{
"cartitems": {
"0": {
"Product_ID": "1",
"quantity": "1",
"cartid": 1
},
"1": {
"Product_ID": "5",
"quantity": "1",
"cartid": 4
},
"2": {
"Product_ID": "5",
"quantity": "1",
"cartid": 6
}
}
}
It adds keys! This only occurs when there are more than 3 items, which baffles me. Is there any way I can re-write my code so that it prevents theses keys from being created?
In PHP, there are only arrays, which are used for both associative and numerically indexed maps/lists/arrays. Javascript/JSON has two distinct concepts: numerically indexed arrays ([...]) and object maps ({ foo : ... }). For PHP's json_encode to decide which to use when encoding an array, there's some logic behind the scenes. Generally, if the array keys are contiguous and all numerical, the array is encoded to a JSON array ([...]). If there's even one key out of order or a non-numeric key, a JSON object is used instead.
Why your array manipulation in particular triggers an object, I don't know. To avoid this though, you can reset your array keys to make sure they're numerically, contiguously indexed:
$_SESSION['cart_items']['cartitems'] = array_values($_SESSION['cart_items']['cartitems']);
Try this, worked for me.
Transfer the array to a new array with auto keys:
/* Remove the item */
foreach ($_SESSION['cart_items']['cartitems'] as $key => $product) {
if ($product['cartid'] == $cartid) {
unset($_SESSION['cart_items']['cartitems'][$key]);
}
}
$var=array();
foreach($_SESSION['cart_items']['cartitems'] as $key => $product) {
$var['cart_items']['cartitems'][] = $product;
}
echo json_encode($var['cart_items']);

PHP - array_search appends keys with json_encode function parsing issue [duplicate]

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...

Categories