Let's say I have a PHP array:
$array1 = array(
'protein' => array('PROT', 100, 150),
'fat' => array('FAT', 100, 250),
'carbs' => arary('CARBS', 10, 20)
);
$array2 = array(
'vitaminA' => array('vitA', 1, 2),
'vitaminB' => array('vitB', 1, 2),
'vitaminC' => arary('vitC', 1, 2)
);
Now I want a combined array of those nutrients (something like array_merge()), but I only need the keys, not the values themselves.
So either this:
$combined = array(
'protein' => NULL,
'fat' => NULL,
'carbs' => NULL,
'vitaminA'=> NULL,
'vitaminB'=> NULL,
'vitaminC'=> NULL
);
OR
$combined = array('protein', 'fat', 'carbs', 'vitaminA', 'vitaminB', 'vitaminC');
I can do this manually with a foreach loop, but I was hoping there's a function which would make this process fast and optimized.
Wouldn't this do the trick?
$combined = array_merge(array_keys($array1), array_keys($array2));
This would result in your option #2.
I haven't done any benchmarks but I know that isset() is faster than in_array() in many cases; something tells me that it will be the same for isset() versus array_key_exists(). If it matters that much, you could try to use this:
$combined = array_flip(array_merge(array_keys($array1), array_keys($array2)));
Which would result in something like this:
$combined = array(
'protein' => 1,
'fat' => 2,
'carbs' => 3,
'vitaminA'=> 4,
'vitaminB'=> 5,
'vitaminC'=> 6
);
That would allow you to use isset(), e.g. option #1.
#edit I did some research on the performance of all three mentioned functions and most, if not all, case studies show that isset() is the fastest of all (1, 2); mainly because it is not actually a function but a language construct.
However do note that we now use array_flip() to be able to use isset(), so we lose quite a few microseconds to flip the array; therefore the total execution time is only decreasing if (and only if) you use isset() quite often.
This:
function array_merge_keys($arr1, $arr2) {
return array_merge(array_keys($arr1), array_keys($arr2));
}
should do the trick.
Related
I have one array:
$array_sorter = [
'XXS' => 1,
'XS' => 2,
'S' => 3,
'M' => 4,
'L' => 5,
'XL' => 6,
'XXL' => 7
];
and another one that could be like this:
$array_to_sort = ('XS','M','XL','S','L','XXS')
how can I do to sort the second one based upon the first one?
I mean:
$array_to_sort are sizes but they are random inside this array
I need to print the list of available sizes ($array_to_sort) but in the order of $array_sorter
As stated by #Jakumi, usort would be the way to go. However this way might be easier to understand if you are a beginner :
$array_sorter = [
'XXS' => 1,
'XS' => 2,
'S' => 3,
'M' => 4,
'L' => 5,
'XL' => 6,
'XXL' => 7,
];
$array_to_sort = array('XS', 'M', 'XL', 'S', 'L', 'XXS');
$array_sorted = array();
foreach($array_to_sort as $item){
$k = $array_sorter[$item];
$array_sorted[$k] = $item;
}
ksort($array_sorted);
Simply use the first array as a "weight"-provider. I mean the first array have a weight for every entry of your second array (e.g. M get the weight 4).
loop over the second array and create a thrid one: $weightArray like this:
$weightArray = array();
foreach($array_to_sort as $value){
$weight = $array_sorter[$value];
if(!isset($weightArray[$weight])){
$weightArray[$weight] = array();
}
$weightArray[$weight][] = $value;
}
Now you have an array like this: (2=>'XS', 4=>'M', 5=>'XL', 3=>'S', 5=>'L', 1=>'XXS')
now you can sort it by key ksort() and copy it back to your source array like this:
$array_to_sort = array_values(ksort($weightArray));
PS: if you have something like $array_to_sort = ('XS','M','M','M','L','XXS') it will also work => $array_to_sort = ('XXS','XS','M','M','M','L')
Thanks to all for you suggestion
I found alone this solution:
my mistake was focusing on the array to be sorted ($arrat_to_sort)
instead I turned the problem:
as $array_sorter always contains all possible values of $arrat_to_sort
in the right order, I used the function array_intersect($ array_sorter, $ arrat_to_sort)
and immediately I have an array with the values of $ arrat_to_sort in the $array_sorter position
This is a quick one. Is there a way to compare a given value to a multidimensional array of preset rules, without looping through our rules?
I'll give you an example. Let's say we are evaluating students from 1 to 10. And we want to assign a performance assessment with a few words. So we'd have ranges of marks and what they represent, for example:
$evaluation = array(
array('from' => 1, 'to' => 3, 'comment' => 'Stop watching TV'),
array('from' => 4, 'to' => 6, 'comment' => 'Keep trying'),
array('from' => 7, 'to' => 8, 'comment' => 'Almost there'),
array('from' => 9, 'to' => 10, 'comment' => 'EMC2')
);
A student got 8, so we'd do:
$grade = 8;
foreach($evaluations as $evaluation) {
if($grade >= $evaluation['from'] && $grade <= $evaluation['to']) {
echo $evaluation['comment'];
}
}
Which I guess is fine. But is there a more neater way to do this? Perhaps a built-in PHP function that would be faster than looping through our set of rules?
Thanks.
A switch statement would be slightly faster but since you are dealing with a 4-element array, almost anything will do.
If you unroll the loop by using a switch statement, you reduce the number of expressions that need to be evaluated since in your loop you need to make a greater than and a less than comparison. Using the switch, you need only one comparison.
There are other PHP functions like array_filter and array_reduce that may lead to the same result if you define the correct callback function, but what you have right now may be as good as anything else.
Think you're over thinking the array a bit, you could key by max value in each range, it's a little less data, and less to evaluate, but in all honesty it seems like a micro optimisation
$evaluation = array(
3 => 'Stop watching TV',
6 => 'Keep trying',
8 => 'Almost there',
10 => 'EMC2'
);
$grade = 8;
foreach ($evaluation as $limit => $comment) {
if ($limit < $grade) continue;
break;
}
echo $comment;
I have two arrays,
The $first has 5000 arrays inside it and looks like:
array(
array('number' => 1),
array('number' => 2),
array('number' => 3),
array('number' => 4),
...
array('number' => 5000)
);
and the $second has 16000 rows and looks like:
array(
array('key' => 1, 'val' => 'something'),
array('key' => 2, 'val' => 'something'),
array('key' => 3, 'val' => 'something'),
...
array('key' => 16000, 'val' => 'something'),
)
I want to create a third array that contains $second[$i]['val'] IF $second[$i][$key] is in $first[$i][$number]
currently I am doing:
$third = array();
foreach($first as &$f)
$f = $f['number'];
foreach($second as $s){
if(in_array($s['key'], $first)
$third[] = $s['val];
}
but, unless I use php's set_timeout(0) it is timing out, is there a more efficient way?
$third = array();
$ftemp = array();
foreach($first as $f)
$ftemp[$f['number']] = true;
foreach($second as $s){
if(isset($ftemp[$s['key']]))
$third[] = $s['val'];
}
should be waaay faster.
Don't try to make lookup dictionary in more convoluted way like below, because it actually is slower than above straightforward loop:
$third = array();
$ftemp = array_flip(reset(call_user_func_array('array_map', array_merge(array(null), $first))));
// $ftemp = array_flip(array_map('reset', $first)); // this is also slower
// array_unshift($first, null); $ftemp = array_flip(reset(call_user_func_array('array_map', $first))); // and this is even slower and modifies $first array
foreach($second as $s){
if(isset($ftemp[$s['key']]))
$third[] = $s['val'];
}
It's probably serious nitpicking but you could replace the foreach with a for which is a little faster, but i doubt that will make a big difference. You are working on a big dataset which might simply be not really fast to process on a webserver.
You are asking for "intersections" between the two arrays but on specific column keys which are not identical. Not to worry, PHP has a native function that is optimized under the hood for this task. array_uintersect() with no special data preparation. Within the custom callback function, null coalesce to the opposite array's key name. The reason for this fallback is because $a and $b do not represent array1 and array2. Because the intersect/diff family of native array functions sort while they filter, there may be instances where column values from the same array will be compared against each other. Again, this is part of the source code optimization.
Code: (Demo)
var_export(
array_uintersect(
$keyValues,
$numbers,
fn($a, $b) => ($a['number'] ?? $a['key']) <=> ($b['number'] ?? $b['key'])
)
);
As a general rule, though, if you are going to make a lot of array comparisons and speed matters, it is better to make key-based comparisons instead of value-based comparisons. Because of the way that PHP handles arrays as hashmaps, key-comparison functions/processes always outpace their value-comparing equivalent.
If you need to isolate the val column data after filtering, array_column() will fix this up for you quickly.
Here's what I am trying to accomplish:
function foo($args) {
switch($args['type']) {
case 'bar':
bar($args['data']); // do something
break;
}
}
// or something like that
Which is basically a way of using named parameters in PHP.
Now, in order to build this $args array, I am forced to write ugly syntax like:
$builtArgs = array('type' => 'bar',
'data' => array(1, 2, 3),
'data2' => array(5, 10, 20)
);
foo($builtArgs);
Which gets uglier as I add more dimensions to the array, and also forces me to write tons of array(...) constructs. Is there a prettier way to do this?
For one thing, it could be done if we could use Python-like syntax:
$buildArgs = {'type' : 'bar', 'data' : [1, 2, 3], 'data2' : [5, 10, 20]};
But it is PHP.
You could create a JSON-encoded string and use json_decode() to convert it into a variable. This has syntax very similar to the Python-like syntax you mentioned.
$argstr = '{"type" : "bar", "data" : [1, 2, 3], "data2" : [5, 10, 20]}';
$buildArgs = json_decode($argstr, true);
EDIT: Updated code to accommodate #therefromhere's suggestion.
No. Alternative syntaxes for creating arrays have been proposed several times (the link lists 5 separate threads in the dev mailing list), but they were rejected.
No, there is no "short-syntax" to write arrays nor objects, in PHP : you have to write all those array().
(At least, there is no such syntax... yet ; might come in a future version of PHP ; who knows ^^ )
But note that have too many imbricated arrays like that will makes things harder for people who will have to call your functions : using real-parameters means auto-completion and type-hinting in the IDE...
There are no alternatives to constructing these nested arrays-- but there are options in how you can format your code that makes it readable. This is strictly preference:
return array
(
'text' => array
(
'name' => 'this is the second',
'this' => 'this is the third',
'newarr' => array
(
'example'
),
)
);
// Or using the long way
$array = array();
$array += array
(
'this' => 'is the first array'
);
In PHP, say that you have an associative array like this:
$pets = array(
"cats" => 1,
"dogs" => 2,
"fish" => 3
);
How would I find the key with the lowest value? Here, I'd be looking for cats.
Is there some built in PHP function that I've missed which does this? It would also be great if there was a solution that accounted for several values being identical, as below:
$pets = array(
"cats" => 1,
"dogs" => 1,
"fish" => 2
);
Above, I wouldn't mind if it just output either; cats or dogs.
Thanks in advance.
array_keys is your friend:
$pets = array(
"cats" => 1,
"dogs" => 2,
"fish" => 3
);
array_keys($pets, min($pets)); # array('cats')
P.S.: there is a dup here somewhere on SO (it had max instead of min, but I can distinctly remember it).
Thats how i did it.
$pets = array(
"cats" => 1,
"dogs" => 2,
"fish" => 3
);
array_search(min($pets), $pets);
I hope that helps
Might try looking into these:
natcasesort(array)
natsort(array)
$min_val = null;
$min_key = null;
foreach($pets as $pet => $val) {
if ($val < $min_val) {
$min_val = $min;
$min_key = $key;
}
}
You can also flip the array and sort it by key:
$flipped = array_flip($pets);
ksort($flipped);
Then the first key is the minimum, and its value is the key in the original array.
Another approach for retrieving a single string is by using a desirable sorting method and retrieving the first key directly by using key() on the sorted array. In this instance the key with the lowest value is desired, asort will sort from lowest to highest values and reset the internal pointer. To retrieve the reverse (highest to lowest) use arsort.
Example: https://3v4l.org/5ijPh
$pets = array(
"dogs" => 2,
"cats" => 1,
"fish" => 3
);
asort($pets);
var_dump(key($pets));
//string(4) "cats"
$pets = array(
"dogs" => 1,
"cats" => 1,
"fish" => 3
);
asort($pets);
var_dump(key($pets));
//string(4) "dogs"
Take note that all of the PHP array sorting methods will alter the array by-reference.
To prevent altering the original array, create a copy of the array or use an Iterator.
$petsSorted = $pets;
asort($petsSorted);
key($petsSorted);
find the highest value
print max(120, 7, 8, 50);
returns --> 120
$array = array(100, 7, 8, 50, 155, 78);
print max($array);
returns --> 155
find the lowest value
print min(120, 7, 8, 50);
returns --> 7
$array = array(50, 7, 8, 101, 5, 78);
print min($array);
returns --> 5