Laravel array_diff attributes [duplicate] - php

This question already has answers here:
how to compare the values of 2 arrays with each other in php and output the differences [duplicate]
(2 answers)
Closed last year.
I'm trying to implement a RecordsActivity trait like in the Birdboard example on Laracasts. Here's Jeffrey's code updated for Laravel >=5.7:
/**
* Fetch the changes to the model.
*
* #return array|null
*/
protected function activityChanges()
{
if ($this->wasChanged()) {
// weirdness here, see below
dump(
$this->oldAttributes,
$this->getAttributes(),
array_diff($this->oldAttributes, $this->getAttributes())
);
return [
'before' => Arr::except(array_diff($this->oldAttributes, $this->getAttributes()), 'updated_at'),
'after' => Arr::except($this->getChanges(), 'updated_at')
];
}
}
The dump() above produces the below response during the test (without annotations). Nothing changes between the two arrays, and it's obvious that one row is different, but somehow array_diff is failing.
// $this->oldAttributes
array:14 [
"name" => "890 Gleichner Lights Suite 446"
"address" => "890 Gleichner Lights Suite 446"
"city" => "Mantetown"
"state" => "CT"
"postal_code" => "00627"
"active" => false
]
// $this->getAttributes()
array:14 [
"name" => "890 Gleichner Lights Suite 446"
"address" => "Changed"
"city" => "Mantetown"
"state" => "CT"
"postal_code" => "00627"
"active" => false
]
// array_diff($this->oldAttributes, $this->getAttributes())
[]

Check the array_diff
array_diff ( array $array1 , array $array2 [, array $... ] ) :array
Compares array1 against one or more other arrays and returns the values in array1 that are not present in any of the other arrays.
So if one field's value is set empty, change the order cannot work too, for example: set the "address" => "", then array_diff($this->getAttributes(), $this->oldAttributes) will return [] too.
array_diff_assoc ( array $array1 , array $array2 [, array $... ] ) : array
Compares array1 against array2 and returns the difference. Unlike array_diff() the array keys are also used in the comparison.
Use array_diff_assoc is better:
$changes = array_diff_assoc($this->oldAttributes(), $this->getAttributes);

Related

Compare multidimensional array with array of keys only

I have a multidimensional array that looks like this:
"fields" => {
"name": "John"
"lastname": "Doe"
}
"data" => {
"person": array:2 [
0 => {
"adress": "foo"
"phone": "bar"
},
1 => {
"adress": "foo1"
"phone": "bar1"
}
]
}
Now I want to check that in this multidimensional array the follow keys exist so my second array to compare looks like this:
[
'fields' => [
"name",
"lastname",
],
"data" => [
"person" => [
"adress",
"phone"
]
],
]
Now here's what I've tried to compare the keys of my second array with the first array:
$result = array_diff_key($firstArray, array_flip($secondArray));
But this gives me an
array_flip(): Can only flip STRING and INTEGER values!
So I need a way to recursively flip the array or maybe I am doing it a bit wrong
I have a multidimensional array that looks like this ...
This is not an array, whatever you've done it now is amorphous trash, not an array not an json object.
$result = array_diff_key($firstArray, array_flip($secondArray));
array_diff_key() finds differences - unique keys
array_flip() replaces keys with values in the array
So I need a way to recursively flip the array or maybe I am doing it a bit wrong
Don't flip arrays, stop searching differences if you want to find the same keys.
You need at least 2 arrays (or objects) to comparison. If you will have 2 (not nested) arrays use
array_intersect_key($array1,$array2);
For nested arrays... you have to get all the keys and then compare.

Compare arays and exclude element form one if it has duplicates in others

I need to compare arrays, if element from first or second array has duplicates in another one I need to exclude it. I know it sound simply and I'm sure it is but i cant handle with that problem :(
So i have first array like this:
Array:3 [
6 => blog/something
4 => blog/somethingElse
5 => blog/else
]
Second array almost identical:
Array:3 [
1 => /blog
2 => /comments
3 => /posts
]
And the last array:
(integer on the left is id of elements in second array, in this example
comments and posts)
Array:2 [
0 => array:2 [
'page_id' => 2
'value' => noindex
]
1 => array:2 [
'page_id' => 3
'value' => noindex
]
]
So if I have element in array first or second which exist in array thrid too AND have value = noindex i need to exclude it.
I have tried do this by foreach recursive, by array_walk_recursive but I still can't get satisfied result
First get all the indices you need to exclude and then exclude them:
$excludeIndices = array_column(array_filter($array3, function ($entry) {
return $entry['value'] === 'noindex';
}), 'page_id');
$keepArray1 = array_diff_key($array1, array_flip($excludeIndices));
$keepArray2 = array_diff_key($array2, array_flip($excludeIndices));
Sandbox
You can filter using the first two arrays directly.
$result = array_filter($last, function($item) use ($first, $second) {
return !($item['value'] == 'noindex' &&
(isset($first[$item['page_id']]) || isset($second[$item['page_id']]))
);
});

Why is array_merge_recursive not recursive?

I recently found a bug in my application caused by unexpected behaviour of array_merge_recursive. Let's take a look at this simple example:
$array1 = [
1 => [
1 => 100,
2 => 200,
],
2 => [
3 => 1000,
],
3 => [
1 => 500
]
];
$array2 = [
3 => [
1 => 500
]
];
array_merge_recursive($array1, $array2);
//returns: array:4 [ 0 => //...
I expected to get an array with 3 elements: keys 1, 2, and 3. However, the function returns an array with keys 0, 1, 2 and 3. So 4 elements, while I expected only 3. When I replace the numbers by their alphabetical equivalents (a, b, c) it returns an array with only 3 elements: a, b and c.
$array1 = [
'a' => [
1 => 100,
2 => 200,
],
'b' => [
3 => 1000,
],
'c' => [
1 => 500
]
];
$array2 = [
'c' => [
1 => 500
]
];
array_merge_recursive($array1, $array2);
//returns: array:3 [ 'a' => //...
This is (to me at least) unexpected behaviour, but at least it's documented:
http://php.net/manual/en/function.array-merge-recursive.php
If the input arrays have the same string keys, then the values for
these keys are merged together into an array, and this is done
recursively, so that if one of the values is an array itself, the
function will merge it with a corresponding entry in another array
too. If, however, the arrays have the same numeric key, the later
value will not overwrite the original value, but will be appended.
The documentation isn't very clear about what 'appended' means. It turns out that elements of $array1 with a numeric key will be treated as indexed elements, so they'll lose there current key: the returned array starts with 0. This will lead to strange outcome when using both numeric and string keys in an array, but let's not blame PHP if you're using a bad practice like that. In my case, the problem was solved by using array_replace_recursive instead, which did the expected trick. ('replace' in that function means replace if exist, append otherwise; naming functions is hard!)
Question 1: recursive or not?
But that's not were this question ends. I thought array_*_resursive would be a recursive function:
Recursion is a kind of function call in which a function calls itself.
Such functions are also called recursive functions. Structural
recursion is a method of problem solving where the solution to a
problem depends on solutions to smaller instances of the same problem.
It turns out it isn't. While $array1 and $array2 are associative arrays, both $array1['c'] and $array2['c'] from the example above are indexed arrays with one element: [1 => 500]. Let's merge them:
array_merge_recursive($array1['c'], $array2['c']);
//output: array:2 [0 => 500, 1 => 500]
This is expected output, because both arrays have a numeric key (1), so the second will be appended to the first. The new array starts with key 0. But let's get back to the very first example:
array_merge_recursive($array1, $array2);
// output:
// array:3 [
// "a" => array:2 [
// 1 => 100
// 2 => 200
// ]
// "b" => array:1 [
// 3 => 1000
// ]
// "c" => array:2 [
// 1 => 500 //<-- why not 0 => 500?
// 2 => 500
// ]
//]
$array2['c'][1] is appended to $array1['c'] but it has keys 1 and 2. Not 0 and 1 in the previous example. The main array and it's sub-arrays are treated differently when handling integer keys.
Question 2: String or integer key makes a big difference.
While writing this question, I found something else. It's getting more confusing when replacing the numeric key with a string key in a sub-array:
$array1 = [
'c' => [
'a' => 500
]
];
$array2 = [
'c' => [
'a' => 500
]
];
array_merge_recursive($array1, $array2);
// output:
// array:1 [
// "c" => array:1 [
// "a" => array:2 [
// 0 => 500
// 1 => 500
// ]
// ]
//]
So using a string key will cast (int) 500 into array(500), while using a integer key won't.
Can someone explain this behaviour?
If we take a step back and observe how array_merge*() functions behave with only one array then we get a glimpse into how it treats associative and indexed arrays differently:
$array1 = [
'k' => [
1 => 100,
2 => 200,
],
2 => [
3 => 1000,
],
'f' => 'gf',
3 => [
1 => 500
],
'99' => 'hi',
5 => 'g'
];
var_dump( array_merge_recursive( $array1 ) );
Output:
array(6) {
["k"]=>
array(2) {
[1]=>
int(100)
[2]=>
int(200)
}
[0]=>
array(1) {
[3]=>
int(1000)
}
["f"]=>
string(2) "gf"
[1]=>
array(1) {
[1]=>
int(500)
}
[2]=>
string(2) "hi"
[3]=>
string(1) "g"
}
As you can see, it took all numeric keys and ignored their actual value and gave them back to you in the sequence in which they were encountered. I would imagine that the function does this on purpose to maintain sanity (or efficiency) within the underlying C code.
Back to your two array example, it took the values of $array1, ordered them, and then appended $array2.
Whether or not this behavior is sane is a totally separate discussion...
You should read the link you provided it states (emphasis mine):
If the input arrays have the same string keys, then the values for these keys are merged together into an array, and this is done recursively, so that if one of the values is an array itself, the function will merge it with a corresponding entry in another array too. If, however, the arrays have the same numeric key, the later value will not overwrite the original value, but will be appended.

Match an array value with a string and return the array PHP

I have an array like this
0 => array:4 [▼
"Name" => "Aroma Therapy - 45 minutes"
"ID" => "1000000015"
"Price" => "50.00"
"Category" => "Aroma"
]
1 => array:4 [▼
"Name" => "Aroma Therapy - 60 Minutes"
"ID" => "0000000003"
"Price" => "100.00"
"Category" => "Aroma"
What I am trying to achieve is that, whenever a user searches for 'therapy' or 'aroma', I want to match with this array's name and return it's price and ID.
I tried using strpos() strstr() and regular expressions as well, I can find if the search matches or not but I cannot get the functionality of returning the array when matched.
So, if a user searches for 'therapy', I want to return the two arrays above, in a variable called $result. So I can access it's name, ID and price like this
$result->Name, $result->ID, $result->Price
Any ideas on how to achieve this kind of functionality?
Using array_filter and stripos(), you can get array items
stripos — Find the position of the first occurrence of a
case-insensitive substring in a string
array_filter — Filters elements of an array using a callback
function
// keyword to be searched
$keyword = 'aroma';
// you will get all array items of matched
$result_array = array_filter($array, function($item) use ($keyword) {
return ( stripos($item['Name'], $keyword) !== false );
});
// to convert to object
$object = json_decode( json_encode($result_array) );

Remove specific records from associative array key in php

Hi I have an array that contains two arrays that has the following structure:
categories [
"lvl0" => array:2 [
0 => "Cleaning"
1 => "Bread"
]
"lvl1" => array:2 [
0 => null
1 => "Bread > rolls"
]
]
I would like to remove any records of NULL from the 'lvl1' array but have not been able to find the correct method to do this.
I have tried:
array_filter($categories['lvl1'])
But this also removes all records associated to lvl1 and not just the NULL ones.
Any help would be greatly appreciated.
Thanks
array_filter() takes a callback as the second argument. If you don't provide it, it returns only records that aren't equal to boolean false. You can provide a simple callback that removes empty values.
array_filter() also uses a copy of your array (rather than a reference), so you need to use the return value.
For instance:
$categories = [
"lvl0" => [
"Cleaning",
"Bread"
],
"lvl1" => [
null,
"Bread > rolls"
]
];
$lvl1 = array_filter($categories['lvl1'], function($value) {
return !empty($value);
});
var_dump($lvl1);
That will return:
array(1) {
[1] =>
string(13) "Bread > rolls"
}
I was having the same issue on my last working day.Generally for associative array array_filter() needs the array key to filter out null, false etc values. But this small function help me to filter out NULL values without knowing the associative array key. Hope this will also help you, https://eval.in/881229
Code:
function array_filter_recursive($input)
{
foreach ($input as &$value)
{
if (is_array($value))
{
$value = array_filter_recursive($value);
}
}
return array_filter($input);
}
$categories = [
"lvl0" => [
"Cleaning",
"Bread"
],
"lvl1" => [
null,
"Bread > rolls"
]
];
$result = array_filter_recursive($categories);
print '<pre>';
print_r($result);
print '</pre>';
Output:
(
[lvl0] => Array
(
[0] => Cleaning
[1] => Bread
)
[lvl1] => Array
(
[1] => Bread > rolls
)
)
Ref: http://php.net/manual/en/function.array-filter.php#87581
Robbie Averill who commented on my post with the following solved the issue:
$categories['lvl1'] = array_filter($categories['lvl1']);

Categories