PHP select array element by ["something_*"] - php

I was wondering about this kind of situation.
What if i have a huge array say about 50k items or more.
Now let's say many of that array keys have prefix let's name it settings_, now if i want to select all values where key begins by settings_ would i need to loop trough all 50k items or is there a better way?
And say there is some "magical" way to do this with single level arrays, what about multidimensional ones?

There is preg_grep, which matches array values. Since you want to search keys, you need to invert keys and values with array_flip:
<?php
$array = array(
'armenia' => 0,
'argentina' => 1,
'brazil' => 2,
'bolivia' => 3,
'congo' => 4,
'denmark' => 5
);
$filtered = array_flip(preg_grep('/^b/', array_flip($array)));
var_dump($filtered);
/*
Output:
array(2) {
["brazil"]=>
int(2)
["bolivia"]=>
int(3)
}
*/

$arr_main_array = array('something_test' => 123, 'other_test' => 456, 'something_result' => 789);
foreach($arr_main_array as $key => $value){
$exp_key = explode('_', $key);
if($exp_key[0] == 'something'){
$arr_result[] = $value;
}
}
if(isset($arr_result)){
print_r($arr_result);
}
You can execute the code at
http://sandbox.onlinephpfunctions.com/code/884816dd115b3ccc610e1732e9716471a7b29b0f

Related

Split an array into two arrays based on identical values

I have this array:
$arrayAll = [
'156' => '1',
'157' => '1',
'158' => '2',
'159' => '1',
'160' => '2',
'161' => '1'
];
where the keys are unique - they don't ever repeat. And the value could be either 1 or 2 - nothing else.
And I need to "split" this $arrayAll array into $array1 - that will contain everything with value 1 and $array2 - that will contain everything with value 2 so in the end I will have:
$array1 = [
'156' => '1',
'157' => '1',
'159' => '1',
'161' => '1'
];
and
$array2 = [
'158' => '2',
'160' => '2'
];
and as you can see, I will have the keys from the original array will remain the same.
What is the simplest thing to do to separate the original array like this?
This is probably the simplest way.
Loop it and create a temporary array to hold the values then extract the values to your array 1 and 2.
Foreach($arrayAll as $k => $v){
$res["array" . $v][$k] = $v;
}
Extract($res);
Var_dump($array1, $array2);
https://3v4l.org/6en6l
Updated to use extract and a method of variable variables.
The update means it will work even if there is a value "3" in the input array.
https://3v4l.org/jbvBf
Use foreach and compare each value and assign it to a new array.
$array1 = [];
$array2 = [];
foreach($arrayAll as $key=>$val){
if($val == 2){
$array2[$key] = $val;
}else{
$array1[$key] = $val;
}
}
print_r($array1);
print_r($array2);
Demo
The simplest thing to do is to use a foreach loop.
$array = [
'156' => '1',
'157' => '1',
'158' => '2',
'159' => '1',
'160' => '2',
'161' => '1'
];
$array1 = [];
$array2 = [];
foreach ($array as $key => $value)
// If value is 1, add to array1
// If value is not 1, add value to array2
if ($value === '1')
$array1[$key] = $value;
else
$array2[$key] = $value;
echo var_dump($array1);
echo '<br>';
echo var_dump($array2);
Use indirect reference:
$arrayAll = array("156"=>"1", "157"=>"1", "158"=>"2", "159"=>"1", "160"=>"2", "161"=>"1");
foreach($arrayAll as $key=>$value) {
$name = "array".$value;
$$name[$key] = $value;
}
echo "<pre>";
print_r($array1);
print_r($array2);
echo "</pre>";
//your array
$yourArray = [
'156' => '1',
'157' => '1',
'158' => '2',
'159' => '1',
'160' => '2',
'161' => '1'
];
With the conditions you mentioned just build two arrays, you can use array_keys with the second parameter that accepts a search value
$array1 = array_keys($yourArray, '1');
$array2 = array_keys($yourArray, '2');
If you don't want to use array_keys, go for an iteration
$array1 = array();
$array2 = array();
foreach($yourArray as $key=>$value){
//will always be 1 or 2, so an if-else is enought
if($value == 1){
$array1[] = $key;
} else {
$array2[] = $key;
}
}
And that's it.
Check this link for array_keys
If you have more than 2 values, the following will work and can be reused for other cases
You want to group them according to the values
$arrayOfValues = array_values($yourArray);
//this returns only the values of the array
$arrayOfUniqueValues = array_unique($arrayOfValues);
//this returns an array with the unique values, also with this u consider
//the posibility for more different values
//also u can get the unique values array on a single line
$arrayIfUniqueValues = array_unique(array_values($yourArray));
The array you will return
$theReturn = array();
foreach($arrayOfUniqueValues as $value ){
//what does this do?
//for each iteration it creates a key in your return array "$theReturn"
//and that one is always equal to the $value of one of the "Unique Values"
//array_keys return the keys of an array, and with the second parameter
//it acceps a search parameter, so the keys it return are the ones
//that matches the value, so here u are getting the array already made
$theReturn[$value] = array_keys($yourArray, $value);
}
The var_dump, in this case, will look like this
array(2) {
[1]=>
array(4) {
[0]=>
int(156)
[1]=>
int(157)
[2]=>
int(159)
[3]=>
int(161)
}
[2]=>
array(2) {
[0]=>
int(158)
[1]=>
int(160)
}
}
Hope my answer helps you, I tried to organize the solutions starting with the shortest/simplest.
Edit:
I forgot you needed the key value too, at least in this solution the array is always referring to the value, like $array1, $array2 or the $key references to the value as in the last solution
The simplest solution for separating an array into others based on the values involves:
Getting its unique values using array_unique.
Looping over these values using foreach.
Getting the key-value pairs intersecting with the current value using array_intersect.
Code:
# Iterate over every value and intersect it with the original array.
foreach(array_unique($arrayAll) as $v) ${"array$v"} = array_intersect($arrayAll, [$v]);
Advantage:
The advantage of this answer when compared to other given answers is the fact that it uses array_unique to find the unique values and therefore iterates only twice instead of n times.
Check out a live demo here.

Shuffle the order of keys in an associative array, if they have the same values?

Given an associative array like this, how can you shuffle the order of keys that have the same value?
array(a => 1,
b => 2, // make b or c ordered first, randomly
c => 2,
d => 4,
e => 5, // make e or f ordered first, randomly
f => 5);
The approach I tried was to turn it into a structure like this and shuffle the values (which are arrays of the original keys) and then flatten it back into the original form. Is there a simpler or cleaner approach? (I'm not worried about efficiency, this is for small data sets.)
array(1 => [a],
2 => [b, c], // shuffle these
4 => [d],
5 => [e, f]); // shuffle these
function array_sort_randomize_equal_values($array) {
$collect_by_value = array();
foreach ($array as $key => $value) {
if (! array_key_exists($value, $collect_by_value)) {
$collect_by_value[$value] = array();
}
// note the &, we want to modify the array, not get a copy
$subarray = &$collect_by_value[$value];
array_push($subarray, $key);
}
arsort($collect_by_value);
$reordered = array();
foreach ($collect_by_value as $value => $array_of_keys) {
// after randomizing keys with the same value, create a new array
shuffle($array_of_keys);
foreach ($array_of_keys as $key) {
array_push($reordered, $value);
}
}
return $reordered;
}
I rewrote the entire code, since I found another way which is a lot simpler and faster than the old one(If you are still interested in the old one see the revision):
old code (100,000 executions): Ø 4.4 sec.
new code (100,000 executions): Ø 1.3 sec.
Explanation
First we get all unique values from the array with array_flip(), since then the values are the keys and you can't have duplicate keys in an array we have our unique values. We also create an array $result for then storing our result in it and $keyPool for storing all keys for each value.
Now we loop through our unique values and get all keys which have the same value into an array with array_keys() and save it in $keyPool with the value as key. We can also right away shuffle() the keys array, so that they are already random:
foreach($uniqueValues as $value => $notNeeded){
$keyPool[$value] = array_keys($arr, $value, TRUE);
shuffle($keyPool[$value]);
}
Now we can already loop through our original array and get a key with array_shift() from the $keyPool for each value and save it in $result:
foreach($arr as $value)
$result[array_shift($keyPool[$value])] = $value;
Since we already shuffled the array the keys already have a random order and we just use array_shift(), so that we can't use the key twice.
Code
<?php
$arr = ["a" => 1, "b" => 1, "c" => 1, "d" => 1, "e" => 1, "f" => 2,
"g" => 1, "h" => 3, "i" => 4, "j" => 5, "k" => 5];
function randomize_duplicate_array_value_keys(array $arr){
$uniqueValues = array_flip($arr);
$result = [];
$keyPool = [];
foreach($uniqueValues as $value => $notNeeded){
$keyPool[$value] = array_keys($arr, $value, TRUE);
shuffle($keyPool[$value]);
}
foreach($arr as $value)
$result[array_shift($keyPool[$value])] = $value;
return $result;
}
$result = randomize_duplicate_array_value_keys($arr);
print_r($result);
?>
(possible) output:
Array (
[b] => 1
[g] => 1
[a] => 1
[e] => 1
[d] => 1
[f] => 2
[c] => 1
[h] => 3
[i] => 4
[k] => 5
[j] => 5
)
Footnotes
I used array_flip() instead of array_unique() to get the unique values from the array, since it's slightly faster.
I also removed the if statement to check if the array has more than one elements and needs to be shuffled, since with and without the if statement the code runs pretty much with the same execution time. I just removed it to make it easier to understand and the code more readable:
if(count($keyPool[$value]) > 1)
shuffle($keyPool[$value]);
You can also make some optimization changes if you want:
Preemptively return, if you get an empty array, e.g.
function randomize_duplicate_array_value_keys(array $arr){
if(empty($arr))
return [];
$uniqueValues = array_flip($arr);
$result = [];
//***
}
Preemptively return the array, if it doesn't have duplicate values:
function randomize_duplicate_array_value_keys(array $arr){
if(empty($arr))
return [];
elseif(empty(array_filter(array_count_values($arr), function($v){return $v > 1;})))
return [];
$uniqueValues = array_flip($arr);
$result = [];
//***
}
Here's another way that iterates through the sorted array while keeping track of the previous value. If the previous value is different than the current one, then the previous value is added to a new array while the current value becomes the previous value. If the current value is the same as the previous value, then depending on the outcome of rand(0,1) either the previous value is added to the new list as before, or the current value is added to the new list first:
<?php
$l = ['a' => 1,'b' => 2, 'c' => 2,
'd' => 4,'e' => 5,'f' => 5];
asort($l);
$prevK = key($l);
$prevV = array_shift($l); //initialize prev to 1st element
$shuffled = [];
foreach($l as $k => $v) {
if($v != $prevV || rand(0,1)) {
$shuffled[$prevK] = $prevV;
$prevK = $k;
$prevV = $v;
}
else {
$shuffled[$k] = $v;
}
}
$shuffled[$prevK] = $prevV;
print_r($shuffled);

How to add subvalues to a new array? [duplicate]

This question already has answers here:
Is there a function to extract a 'column' from an array in PHP?
(15 answers)
Closed 5 months ago.
I have an array with every containing value being another array with one value. I was looking for a way to flatten the array and succeeded but somehow I am having this feeling that it can be done better. Is this the best way or can it still be improved?
<?php
$array = array(
'1' => array('id' => '123'),
'2' => array('id' => '22'),
'3' => array('id' => '133'),
'4' => array('id' => '143'),
'5' => array('id' => '153'),
);
array_walk_recursive($array, function($v, $k) use (&$result) {
$result[] = $v;
});
You can achieve that using the array_map function:
$func = function($value) {
return $value['id'];
};
$array2 = array_map($func, $array);
Or if you want to keep it in one line do:
$array2 = array_map(function($value) { return $value['id']; }, $array);
This will return the array flattened and keeps your initial keys:
array(5) {
[1]=>
string(3) "123"
[2]=>
string(2) "22"
[3]=>
string(3) "133"
[4]=>
string(3) "143"
[5]=>
string(3) "153"
}
If you don't want to keep the keys, then call the following at the end:
$array2 = array_values($array2);
You can use array_map() passing null as the first argument while unpacking the passed array with the splat operator and grab the first element of the result:
$result = array_map(null, ...$array)[0];
Another option would be to use array_column() which creates an array from a single column of a given multidimensional array - feed it your array and column key, e.g.:
$result = array_column($array, 'id');
Output for either:
print_r($result);
Array
(
[0] => 123
[1] => 22
[2] => 133
[3] => 143
[4] => 153
)
This is what I would do. Its cleaner:
$array = array(
'1' => array('id' => '123'),
'2' => array('id' => '22'),
'3' => array('id' => '133'),
'4' => array('id' => '143'),
'5' => array('id' => '153'),
);
foreach($array as $key => $arr){
$result[] = $arr['id'];
}
If the depth won't ever change a foreach loop would likely be faster. That anonymous function has some overhead to it and it really shows the longer your array gets.
If the depth is variable as well, however, then this is the fastest way to traverse and flatten.

php -Merging an Array

I have two array which i want to merge in a specific way in php.
So i need your help in helping me with it as i tried and failed.
So say i have two arrays:
$array1= array(
"foo" => 3,
"bar" => 2,
"random1" => 4,
);
$array2= array(
"random2" => 3,
"random3" => 4,
"foo" => 6,
);
Now when during merging i would like the common key's values to be added.
So like foo exists in array1 and in array2 so when merging array1 with array 2 i should get "foo" => "9"
I better illustration would be the final array which looks like this:
$array1= array(
"foo" => 9,
"bar" => 2,
"random1" => 4,
"random2" => 3,
"random3" => 4,
);
So again i would like the values of the common keys to be added together and non common keys to be added to array or a new array
I hope i was clear enough
Thanks,
Vidhu
Something like that:
function mergeValues() {
$result = array();
$arraysToMerge = func_get_args();
foreach ($arraysToMerge as $array) {
foreach($array as $key => $value) {
$result[$key] += $value;
}
}
return $result;
}
$res = mergeValues($array1, $array2, $array3); // Can pass any ammount of arrays to a function.
foreach($array1 as $k => $v)
{
If (isset($array2[$k]))
$array1[$k] += $array2[$k];
}
foreach($array2 as $k => $v)
{
If (!isset($array1[$k]))
$array1[$k] = $array2[$k];
}

array_splice() - Numerical Offsets of Associative Arrays

I'm trying to do something but I can't find any solution, I'm also having some trouble putting it into works so here is a sample code, maybe it'll be enough to demonstrate what I'm aiming for:
$input = array
(
'who' => 'me',
'what' => 'car',
'more' => 'car',
'when' => 'today',
);
Now, I want to use array_splice() to remove (and return) one element from the array:
$spliced = key(array_splice($input, 2, 1)); // I'm only interested in the key...
The above will remove and return 1 element (third argument) from $input (first argument), at offset 2 (second argument), so $spliced will hold the value more.
I'll be iterating over $input with a foreach loop, I know the key to be spliced but the problem is I don't know its numerical offset and since array_splice only accepts integers I don't know what to do.
A very dull example:
$result = array();
foreach ($input as $key => $value)
{
if ($key == 'more')
{
// Remove the index "more" from $input and add it to $result.
$result[] = key(array_splice($input, 2 /* How do I know its 2? */, 1));
}
}
I first though of using array_search() but it's pointless since it'll return the associative index....
How do I determine the numerical offset of a associative index?
Just grabbing and unsetting the value is a much better approach (and likely faster too), but anyway, you can just count along
$result = array();
$idx = 0; // init offset
foreach ($input as $key => $value)
{
if ($key == 'more')
{
// Remove the index "more" from $input and add it to $result.
$result[] = key(array_splice($input, $idx, 1));
}
$idx++; // count offset
}
print_R($result);
print_R($input);
gives
Array
(
[0] => more
)
Array
(
[who] => me
[what] => car
[when] => today
)
BUT Technically speaking an associative key has no numerical index. If the input array was
$input = array
(
'who' => 'me',
'what' => 'car',
'more' => 'car',
'when' => 'today',
'foo', 'bar', 'baz'
);
then index 2 is "baz". But since array_slice accepts an offset, which is not the same as a numeric key, it uses the element found at that position in the array (in order the elements appear), which is why counting along works.
On a sidenote, with numeric keys in the array, you'd get funny results, because you are testing for equality instead of identity. Make it $key === 'more' instead to prevent 'more' getting typecasted. Since associative keys are unique you could also return after 'more' was found, because checking subsequent keys is pointless. But really:
if(array_key_exists('more', $input)) unset($input['more']);
I found the solution:
$offset = array_search('more', array_keys($input)); // 2
Even with "funny" arrays, such as:
$input = array
(
'who' => 'me',
'what' => 'car',
'more' => 'car',
'when' => 'today',
'foo', 'bar', 'baz'
);
This:
echo '<pre>';
print_r(array_keys($input));
echo '</pre>';
Correctly outputs this:
Array
(
[0] => who
[1] => what
[2] => more
[3] => when
[4] => 0
[5] => 1
[6] => 2
)
It's a trivial solution but somewhat obscure to get there.
I appreciate all the help from everyone. =)
$i = 0;
foreach ($input as $key => $value)
{
if ($key == 'more')
{
// Remove the index "more" from $input and add it to $result.
$result[] = key(array_splice($input, $i , 1));
}
$i++;
}

Categories