php :: one function to do array_unique(array_merge($a,$b)); - php

I am aware that I can use array_unique(array_merge($a,$b));
to merge two arrays and then remove any duplicates,
but,
is there an individual function that will do this for me?
(I know I could write one myself that just calls these, but I am just wondering).

There is no such function. Programming languages in general give you a certain set of tools (functions) and you can then combine them to get the results you want.
There really is no point in creating a new function for every use case there is, unless it is a very common use case - and yours does not seem to be one.

No, array_unique(array_merge($a,$b)); is the way to do it.
See the list of array functions.

By default no there isn't. You can do it another way (which might not be that smart though)
$array1 = array('abc', 'def');
$array2 = array('zxd', 'asdf');
$newarray = array_unique(array($array1, $array2));
does sort of the same thing. Just wondering why isn't what you posted good enough?

//usage $arr1=array(0=>"value0", 1=>"value1", 2=>"value2"); $arr2=array(0=>"value3", 1=>"value4", 2=>"value0") => $result=mergeArrays($arr1,$arr2); $result=Array ( [0] => value0 [1] => value1 [2] => value2 [3] => value3 [4] => value4 )
function mergeArrays () {
$result=array();
$params=func_get_args();
if ($params) foreach ($params as $param) {
foreach ($param as $v) $result[]=$v;
}
$result=array_unique($result);
return $result;
}

In php 5.6+ you can also use the new argument unpacking to avoid multiple array_merge calls (which significantly speed up your code): https://3v4l.org/arFvf
<?php
$array = [
[1 => 'french', 2 => 'dutch'],
[1 => 'french', 3 => 'english'],
[1 => 'dutch'],
[4 => 'swedish'],
];
var_dump(array_unique(array_merge(...$array)));
Outputs:
array(4) {
[0]=>
string(6) "french"
[1]=>
string(5) "dutch"
[3]=>
string(7) "english"
[5]=>
string(7) "swedish"
}

Related

Extract item from multi-dimensional array from custom depth

I have multidimensional array like this:
$obj = array(
"a" => array(
"aa" => array(
"aaa" => 1
),
"bb" => 2,
),
"b" => array(
"ba" => 3,
"bb" => 4,
),
"c" => array(
"ca" => 5,
"cb" => 6,
),
);
I can not figured out a neatest way, e.g. custom-depth function, to extract item at specific location with arguments to function (or array of key names). For example:
echo $obj[someFunc("a", "aa", "aaa")];
... should return 1.
print_r($obj[someFunc("a")]);
... should return:
Array
(
[aa] => Array
(
[aaa] => 1
)
[bb] => 2
)
What is the best way to accomplished this with php7 features?
Since PHP 5.6, ["Variadic functions"][1] have existed. These provide us with a nice simple to read way to collect arguments used in calling a function into a single array. For example:
function getValue(...$parts) {
var_dump($parts);
}
getValue('test', 'part');
Will output:
array(2) {
[0]=>
string(4) "test"
[1]=>
string(4) "part"
}
This was all possible before using built-in functions to get the parameters, but this is more readable.
You could also be a little more explicit with the argument types, but I'll leave that for you to figure out if necessary.
You next major challenge is to loop through the arguments. Something like this will produce the value that you desire.
function getValue(...$parts) {
$returnValue = $obj;
foreach ($parts as $part) {
$returnValue = $obj[$part];
}
return $returnValue;
}
However, this is rather crude code and will error when you try calling it to access non-existent parts. Have a play and fix those bits.
[1]: https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list

Sorting an array by the values of another array

I have a some messy data coming in from a feed, and am trying to figure out how to sort it correctly. I posted a simplified example below. I'd like to sort the people array alphabetically by the Group name.
$people = array(
"category_id_1" => array (
"Mark",
"Jenny",
"Andrew"
),
"category_id_2" => array (
"John",
"Lewis",
"Andrea"
),
"category_id_3" => array (
"Hannah",
"Angie",
"Raleigh"
)
);
$categories = array(
"category_id_1" => "Group B",
"category_id_2" => "Group C",
"category_id_3" => "Group A"
);
Ideally, the end result would be
$people = array(
"category_id_3" => array ( // Group A
"Hannah",
"Angie",
"Raleigh"
),
"category_id_1" => array ( // Group B
"Mark",
"Jenny",
"Andrew"
),
"category_id_2" => array ( // Group C
"John",
"Lewis",
"Andrea"
)
);
I've been spinning my wheels for a while now, and the closest I have gotten is this uasort, which still isn't doing the trick.
uasort($people, function ($a, $b) {
return strcmp($categories[$a], $categories[$b]);
});
Thanks so much for any help.
This can be achieved in a simpler way by taking advantage of array_replace:
// Work on a copy just to be sure the rest of your code is not affected
$temp_categories = $categories;
// Sort categories by name
asort($temp_categories);
// Replace the values of the sorted array with the ones in $people
$ordered_people = array_replace($temp_categories, $people);
You want to sort $people by its keys not its values. You can use uksort for this. Additionally you need to make $categories available in your function. I prefer use for that; but you could also make it a global variable. Final code:
uksort($people, function ($a,$b) use ($categories) {
return strcmp($categories[$a], $categories[$b]);
});
Manual for uksort
use language construct. Before example 3.
I think what you need is to Asort categories and the use that sorted array in a foreach.
Asort($categories);
Foreach($categories as $key => $group){
$new[$key] =$people[$key];
}
Var_dump($new);
https://3v4l.org/kDAQW
Output:
array(3) {
["category_id_3"]=> array(3) {
[0]=> "Hannah"
[1]=> "Angie"
[2]=> "Raleigh"
}
["category_id_1"]=> array(3) {
[0]=> "Mark"
[1]=> "Jenny"
[2]=> "Andrew"
}
["category_id_2"]=>array(3) {
[0]=> "John"
[1]=> "Lewis"
[2]=> "Andrea"
}
}
Try this(tested and working):
asort($categories);
$sorted = array();
foreach ($categories as $key => $value)
$sorted[$key]=$people[$key];
A better shorter approach:(tested and working)
asort($categories);
$result = array_merge($categories,$people);
The second method takes advantage of the fact that array_merge function replace the values in the first array with those in the second one when keys are the same.
warning : The second approach will not work if the keys are numbers. Use only string keys with it. Furthermore if the categories array has entries without corresponding entries in the people array they will be copied to the result
To solve this problem we use array_replace :
asort($categories);
$result = array_replace($categories,$people);
Var_dump($result);// tested and working

extract selected array values based on list php

I have a list of array keys in form of a string
"hasAddStrict,freqItems,freqAmount,freqUnit,freqFirstDayOfWeek"
Now, want to extract only those values, i.e $myarray['hasAddstrict'] should become $hasAddStrict, etc.
Is there a short way to do it? My rather dirty solution:
$ff = explode(',' ,"hasAddStrict,freqItems,freqAmount,freqUnit,freqFirstDayOfWeek");
foreach ( $ff as $key){ $ff[$key] = $SERVICE[$key] ; }
extract($ff);
I agree with Jeff that there isn't really a better way, and that keeping the variables as an array is probably a better idea, but you could wrap it up in a function for convenience (tested in php 5.6):
<?php
function array_extract(array $assoc_array, $keys_list)
{
return array_intersect_key($assoc_array, array_flip(explode(',', $keys_list)));
}
extract(array_extract($SERVICE, "hasAddStrict,freqItems,freqAmount,freqUnit,freqFirstDayOfWeek"));
When tested as follows:
$SERVICE = [
'hasAddStrict' => 123,
'freqItems' => 456,
'freqAmount' => 789,
'freqUnit' => 'abc',
'freqFirstDayOfWeek' => 'def',
];
extract(array_extract($SERVICE, "hasAddStrict,freqItems,freqAmount,freqUnit,freqFirstDayOfWeek"));
var_dump($hasAddStrict, $freqItems, $freqAmount, $freqUnit, $freqFirstDayOfWeek);
You get output:
int(123)
int(456)
int(789)
string(3) "abc"
string(3) "def"

php, array_merge_recursive works well with string keys only

$array1 = [
'1' => '11',
'b' => 1,
3 => 33,
8 => 8
];
$array2 = [
'1' => '22',
'b' => 2,
3 => 44,
9 => 9
];
$merged = array_merge_recursive($array1, $array2);
and the result is:
array(7) {
[0]=>
string(2) "11"
["b"]=>
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
[1]=>
int(33)
[2]=>
int(8)
[3]=>
string(2) "22"
[4]=>
int(44)
[5]=>
int(9)
}
so lets take a glance: the only part is the 'b' keys, they are actually works. I dont want to overwrite anything of it but putting them together to an array. Thats good! But then keys the other numeric keys (int or string) are screwed up.
I want to have this as result:
[
'1' => ['11', '22']
'b' => [1, 2]
[3] => [33, 44]
[8] => 8,
[9] => 9
]
possible?
EDIT: of course keys "1" and 1 - string vs int key are the same
Let's break down this question into to separate problems:
When a key in the second array exist in the first array, you want to create an array and make the value the first element of that array.
To be honest, I don't know an easy way of solving this. I'm not sure there is one. And even if, I'm not sure you really want it. Such a function will lead to arrays having values that are a string or an array. How would you handle such an array?
Update: Well, there is one. Your example already shows that array_merge_recursive will convert values with a string key into an array. So 1 => 'foo' would be 0 => 'foo', but 'foo' => 'bar' will end up as 'foo' => ['bar']. I really don't understand this behaviour.
Using string keys would help you out in this case, but after learning more about array_merge_recursive I decided to avoid this function when possible. After I asked this question someone filed it as a bug in it since PHP 7.0, since it works differently in PHP 5.x.
You want to keep the keys, while array_merge_resursive doesn't preserve integer keys, while it does for integer keys:
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.
To make it worse, it handles differently when handling the nested arrays:
$array1 = [30 => [500 => 'foo', 600 => 'bar']];
$array2 = [];
array_merge_recursive($array1, $array2);
//[0 => [500=> 'foo', 600 => 'bar']];
So effectively, array_merge_resursive isn't really resursive.
Using array_replace_resursive solves that problem:
array_replace_recursive($array1, $array2);
//output:
//array:5 [
// 1 => "22"
// "b" => 2
// 3 => 44
// 8 => 8
// 9 => 9
//]
Please note that PHP is very consistent in being inconsistent. While array_merge_recursive isn't recursive, array_replace_recursive doesn't replace (it also appends):
If the key exists in the second array, and not the first, it will be
created in the first array.
In many cases this is desired behavior and since naming functions isn't PHP's strongest point, you can consider this as a minor issue.
Can you rely on a native function to return your exact desired output? No. At least not in any version as of the date of this post (upto PHP8.1).
The good news is that crafting your own solution is very simple.
Code: (Demo)
foreach ($array2 as $key => $value) {
if (!key_exists($key, $array1)) {
$array1[$key] = $value;
} else {
$array1[$key] = (array)$array1[$key];
$array1[$key][] = $value;
}
}
var_export($array1);
I suppose I am less inclined to recommend this output structure because you have potentially different datatypes on a given level. If you were building subsequent code to iterate this data, you'd need to write conditions on every level to see if the data was iterable -- it just feels like you are setting yourself up for code bloat/convolution. I'd prefer a result which has consistent depth and datatypes.

PHP Remove elements from associative array

I have an PHP array that looks something like this:
Index Key Value
[0] 1 Awaiting for Confirmation
[1] 2 Assigned
[2] 3 In Progress
[3] 4 Completed
[4] 5 Mark As Spam
When I var_dump the array values i get this:
array(5) { [0]=> array(2) { ["key"]=> string(1) "1" ["value"]=> string(25) "Awaiting for Confirmation" } [1]=> array(2) { ["key"]=> string(1) "2" ["value"]=> string(9) "Assigned" } [2]=> array(2) { ["key"]=> string(1) "3" ["value"]=> string(11) "In Progress" } [3]=> array(2) { ["key"]=> string(1) "4" ["value"]=> string(9) "Completed" } [4]=> array(2) { ["key"]=> string(1) "5" ["value"]=> string(12) "Mark As Spam" } }
I wanted to remove Completed and Mark As Spam. I know I can unset[$array[3],$array[4]), but the problem is that sometimes the index number can be different.
Is there a way to remove them by matching the value name instead of the key value?
Your array is quite strange : why not just use the key as index, and the value as... the value ?
Wouldn't it be a lot easier if your array was declared like this :
$array = array(
1 => 'Awaiting for Confirmation',
2 => 'Asssigned',
3 => 'In Progress',
4 => 'Completed',
5 => 'Mark As Spam',
);
That would allow you to use your values of key as indexes to access the array...
And you'd be able to use functions to search on the values, such as array_search() :
$indexCompleted = array_search('Completed', $array);
unset($array[$indexCompleted]);
$indexSpam = array_search('Mark As Spam', $array);
unset($array[$indexSpam]);
var_dump($array);
Easier than with your array, no ?
Instead, with your array that looks like this :
$array = array(
array('key' => 1, 'value' => 'Awaiting for Confirmation'),
array('key' => 2, 'value' => 'Asssigned'),
array('key' => 3, 'value' => 'In Progress'),
array('key' => 4, 'value' => 'Completed'),
array('key' => 5, 'value' => 'Mark As Spam'),
);
You'll have to loop over all items, to analyse the value, and unset the right items :
foreach ($array as $index => $data) {
if ($data['value'] == 'Completed' || $data['value'] == 'Mark As Spam') {
unset($array[$index]);
}
}
var_dump($array);
Even if do-able, it's not that simple... and I insist : can you not change the format of your array, to work with a simpler key/value system ?
...
$array = array(
1 => 'Awaiting for Confirmation',
2 => 'Asssigned',
3 => 'In Progress',
4 => 'Completed',
5 => 'Mark As Spam',
);
return array_values($array);
...
$key = array_search("Mark As Spam", $array);
unset($array[$key]);
For 2D arrays...
$remove = array("Mark As Spam", "Completed");
foreach($arrays as $array){
foreach($array as $key => $value){
if(in_array($value, $remove)) unset($array[$key]);
}
}
You can use this
unset($dataArray['key']);
Why do not use array_diff?
$array = array(
1 => 'Awaiting for Confirmation',
2 => 'Asssigned',
3 => 'In Progress',
4 => 'Completed',
5 => 'Mark As Spam',
);
$to_delete = array('Completed', 'Mark As Spam');
$array = array_diff($array, $to_delete);
Just note that your array would be reindexed.
Try this:
$keys = array_keys($array, "Completed");
/edit
As mentioned by JohnP, this method only works for non-nested arrays.
I kinda disagree with the accepted answer. Sometimes an application architecture doesn't want you to mess with the array id, or makes it inconvenient. For instance, I use CakePHP quite a lot, and a database query returns the primary key as a value in each record, very similar to the above.
Assuming the array is not stupidly large, I would use array_filter. This will create a copy of the array, minus the records you want to remove, which you can assign back to the original array variable.
Although this may seem inefficient it's actually very much in vogue these days to have variables be immutable, and the fact that most php array functions return a new array rather than futzing with the original implies that PHP kinda wants you to do this too. And the more you work with arrays, and realize how difficult and annoying the unset() function is, this approach makes a lot of sense.
Anyway:
$my_array = array_filter($my_array,
function($el) {
return $el["value"]!="Completed" && $el!["value"]!="Marked as Spam";
});
You can use whatever inclusion logic (eg. your id field) in the embedded function that you want.
The way to do this to take your nested target array and copy it in single step to a non-nested array.
Delete the key(s) and then assign the final trimmed array to the nested node of the earlier array.
Here is a code to make it simple:
$temp_array = $list['resultset'][0];
unset($temp_array['badkey1']);
unset($temp_array['badkey2']);
$list['resultset'][0] = $temp_array;
for single array Item use reset($item)

Categories