Related
I have the following code :
$json = json_decode(URL, true);
foreach($json as $var)
{
if($var[id] == $valdefined)
{
$number = $var[count];
}
}
With json it looks like this :
[{"id":"1","count":"77937"},
{"id":"2","count":"20"},
{"id":"4","count":"25"},
{"id":"5","count":"11365"}]
This is what the array ($json) looks like after jsondecode
Array ( [0] => Array ( [id] => 1 [count] => 77937 ) [1] => Array ( [id] => 2 [count] => 20 ) [2] => Array ( [id] => 4 [count] => 25 ) [3] => Array ( [id] => 5 [count] => 11365) )
is there a way to say what is $json[count] where $json[id] = 3 for example
I'm not sure about a better way, but this is also fine, provided the JSON object is not huge. php is pretty fast when looping through JSON. If the object is huge, then you may want to split it. What I personally do is make my JSON into an array of normal objects, sort them, and then searching is faster on sorted items.
EDIT
Do json_decode($your_thing, true); set it true to make it an associative array, and then the id would be key and and the count would be value. After you do this, getting the value with the ID should really be easy and far more efficient.
If you change the way you build your json object to look like this :-
{"1":77937,"2":20,"4":25,"5":11365}
And then use the json_decode() parameter 2 set to TRUE i.e. turn the json into an array.
Then you have a usable assoc array with the ID as the key like so:
<?php
$json = '{"1":77937,"2":20,"4":25,"5":11365}';
$json_array = json_decode($json, TRUE);
print_r( $json_array);
?>
Resulting in this array
Array
(
[1] => 77937
[2] => 20
[4] => 25
[5] => 11365
)
Which you can do a simple
$number = json_array( $valdefined );
Or better still
if ( array_key_exists( $valdefined, $json_array ) ) {
$number = json_array( $valdefined );
} else {
$number = NULL; // or whatever value indicates its NON-EXISTANCE
}
Short answer to your initial question: why can't you write $json['count'] where $json['id'] = 3? Simply because PHP isn't a query language. The way you formulated the question reads like a simple SQL select query. SQL will traverse its indexes, and (if needs must) will perform a full table scan, too, its Structured Query Language merely enables you not to bother writing out the loops the DB will perform.
It's not that, because you don't write a loop, there is no loop (the absence of evidence is not the evidence of absence). I'm not going to go all Turing on you, but there's only so many things we can do on a machine level. On the lower levels, you just have to take it one step at a time. Often, this means incrementing, checking and incrementing again... AKA recursing and traversing.
PHP will think it understands what you mean by $json['id'], and it'll think you mean for it to return the value that is referenced by id, in the array $json, whereas you actually want $json[n]['id'] to be fetched. To determine n, you'll have to write a loop of sorts. Some have suggested sorting the array. That, too, like any other array_* function that maps/filters/merges means looping over the entire array. There is just no way around that. Since there is no out-of-the-box core function that does exactly what you need to do, you're going to have to write the loop yourself.
If performance is important to you, you can write a more efficient loop. Below, you can find a slightly less brute loop, a semi Interpolation search. You could use ternary search here, too, implementing that is something you can work on.
for ($i = 1, $j = count($bar), $h = round($j/2);$i<$j;$i+= $h)
{
if ($bar[++$i]->id === $search || $bar[--$i]->id === $search || $bar[--$i]->id === $search)
{//thans to short-circuit evaluation, we can check 3 offsets in one go
$found = $bar[$i];
break;
}//++$i, --$i, --$i ==> $i === $i -1, increment again:
if ($bar[++$i]->id > $search)
{// too far
$i -= $h;//return to previous offset, step will be halved
}
else
{//not far enough
$h = $j - $i;//set step the remaining length, will be halved
}
$h = round($h/2);//halve step, and round, in case $h%2 === 1
//optional:
if(($i + $h + 1) === $j)
{//avoid overflow
$h -= 1;
}
}
Where $bar is your json-decoded array.
How this works exactly is explained below, as are the downsides of this approach, but for now, more relevant to your question: how to implement:
function lookup(array $arr, $p, $val)
{
$j = count($arr);
if ($arr[$j-1]->{$p} < $val)
{//highest id is still less value is still less than $val:
return (object) array($p => $val, 'count' => 0, 'error' => 'out of bounds');
}
if ($arr[$j-1]->{$p} === $val)
{//the last element is the one we're looking for?
return $end;
}
if ($arr[0]->{$p} > $val)
{//the lowest value is still higher than the requested value?
return (object) array($p => $val, 'count' => 0, 'error' => 'underflow');
}
for ($i = 1, $h = round($j/2);$i<$j;$i+= $h)
{
if ($arr[++$i]->{$p} === $val || $arr[--$i]->{$p} === $val || $arr[--$i]->{$p} === $val)
{//checks offsets 2, 1, 0 respectively on first iteration
return $arr[$i];
}
if ($arr[$i++]->{$p} < $val && $arr[$i]->{$p} > $val)
{//requested value is in between? don't bother, it won't exist, then
return (object)array($p => $val, 'count' => 0, 'error' => 'does not exist');
}
if ($arr[++$i]->{$p} > $val)
{
$i -= $h;
}
else
{
$h = ($j - $i);
}
$h = round($h/2);
}
}
$count = lookup($json, 'id', 3);
echo $count['count'];
//or if you have the latest version of php
$count = (lookup($json, 'id', 3))['count'];//you'll have to return default value for this one
Personally, I wouldn't return a default-object if the property-value pair wasn't found, I'd either return null or throw a RuntimeException, but that's for you to decide.
The loop basically works like this:
On each iteration, the objects at offset $i, $i+1 and $i-1 are checked.
If the object is found, a reference to it is assigned to $found and the loop ends
The object isn't found. Do either one of these two steps:
ID at offset is greater than the one we're looking for, subtract step ($h) from offset $i, and halve the step. Loop again
ID is smaller than search (we're not there yet): change step to half of the remaining length of the array
A diagram will show why this is a more "clever" way of looping:
|==========x=============================|//suppose x is what we need, offset 11 of a total length 40:
//iteration 1:
012 //checked offsets, not found
|==========x=============================|
//offset + 40/2 == 21
//iteration 2:
012//offsets 20, 21 and 22, not found, too far
|==========x=============================|
//offset - 21 + round(21/2)~>11 === 12
//iteration 3:
123 //checks offsets 11, 12, 13) ==> FOUND
|==========x=============================|
assign offset-1
break;
Instead of 11 iterations, we've managed to find the object we needed after a mere 3 iterations! Though this loop is somewhat more expensive (there's more computation involved), the downsides rarely outweigh the benefits.
This loop, as it stands, though, has a few blind-spots, so in rare cases it will be slower, but on average it performs pretty well. I've tested this loop a couple of times, with an array containing 100,000 objects, looking for id random(1,99999) and I haven't seen it take more time than .08ms, on average, it manages .0018ms, which is not bad at all.
Of course, you can improve on the loop by using the difference between the id at the offset, and the searched id, or break if id at offset $i is greater than the search value and the id at offset $i-1 is less than the search-value to avoid infinite loops. On the whole, though, this is the most scalable and performant loopup algorithm provided here so far.
Check the basic codepad in action here
Codepad with loop wrapped in a function
This question already has answers here:
How to filter an associative array comparing keys with values in an indexed array?
(12 answers)
Closed 4 months ago.
Here is data
$array = array(
'random' => 1,
'pewpew' => 2,
'temp' => 5,
'xoxo' => 3,
'qweqweqe' => 4,
);
$fields = array('random', 'xoxo', 'temp');
I need to get in result:
$result = array(
'random' => 1,
'xoxo' => 3,
'temp' => 5,
);
I mean keys presence/order from $fields apply to $array.
The question is:
Can I perform this transformation using only array_ functions? (I don't wanna use iteations)
If yes: can you link me function that I need?
(sorry for spelling mistakes)
upd.
PHP 5.2
$result=array_intersect_key($array ,array_flip($fields) );
// little trick required here...
$fields = array('random' => 0, 'xoxo' => 0, 'temp' => 0);
$result = array_intersect_key($array,$fields);
I am always interested in these types of question, where it's about efficient code (both in code-usage and speed). That said, I tried and benchmarked several different methods, and nothing was as efficient and as a simple foreach!
I tried all the solutions posted, and my own array_walk and basic foreach. I ran several test, both with the arrays and fields posted by Miraage, and some with much larger arrays. I also noted anything odd with the results, such as additional values if $fields had values not in $array.
I've ordered it by speed.
FOREACH: 0.01245 sec
$result = array();
foreach ($fields as $k)
{
if (isset($array[$k]))
$result[$k] = $array[$k];
}
ARRAY_DIFF_KEY: 0.01471 sec (unexpected results: additional values)
$result = array_diff_key($fields, $array);
FOREACH (function): 0.02449 sec
function array_filter_by_key($array, $fields)
{
$result = array();
foreach ($fields as $k)
{
if (isset($array[$k]))
$result[$k] = $array[$k];
}
return $result;
}
ARRAY_WALK (by reference): 0.09123 sec
function array_walk_filter_by_key($item, $key, $vars)
{
if (isset($vars[1][$item]))
$vars[0][$item] = $vars[1][$item];
}
$result = array();
array_walk($fields, 'array_walk_filter_by_key', array(&$result, &$array));
LIST/EACH: 0.12456 sec
$result = array();
reset($fields);
while (list($key, $value) = each($fields))
{
if (isset($array[$value]))
$result[$value] = $array[$value];
}
ARRAY_INTERSECT_KEY: 0.27264 sec (incorrect order)
$result = array_intersect_key($array, array_flip($fields));
ARRAY_REPLACE (array_intersect_key second): 0.29409 sec (unexpected results: additional values)
$result = array_replace(
array_fill_keys($fields, false),
array_intersect_key($array, array_flip($fields))
);
ARRAY_REPLACE (two array_intersect_key): 0.33311 sec
$flip = array_flip($fields);
$result = array_replace(
array_intersect_key($flip, $array),
array_intersect_key($array, $flip)
);
ARRAY_WALK (set null): 3.35929 sec (unexpected results: additional values)
function array_walk_filter_by_key_null(&$item, $key, $array)
{
if (isset($array[$key]))
$item = $array[$key];
else
$item = null;
}
$result = array_flip($fields);
array_walk($result, 'array_walk_filter_by_key_null', $array);
ARRAY_REPLACE (array_intersect_key first): 11.11044 sec
$flip = array_flip($fields);
$result = array_intersect_key(
array_replace($flip, $array),
array_intersect_key($flip, $array)
);
ARRAY_MERGE: 14.11296 sec (unexpected results: additional values)
$result = array_splice(
array_merge(array_flip($fields), $array),
0,
count($fields)
);
So there it is. Can't beat a DIY. Sometimes the perception is that the built-in functions are faster, but it's not always the case. Compilers are pretty good these days.
This code preserves order and works in PHP 5.2 as required
One line:
$result = array_merge( array_flip($fields),
array_intersect_key(
$array,
array_flip( $fields )
)
);
For performance:
$flip = array_flip($fields);
$result = array_merge( $flip
array_intersect_key(
$array,
$flip
)
);
I believe this works as you require.
$result = array_splice(array_merge(array_flip($fields) , $array) , 0 , count($fields));
Just to solve the puzzle:
$result = array_replace(
array_intersect_key(array_flip($fields), $array),
array_intersect_key($array, array_flip($fields))
);
The first array_intersect creates the list of fields in good order, the other one overcomes array_replace functionality to create the the keys that do not exist in the first array.
Meets your requirements. But I wouldn't use it in any production code, as this may be pretty heavy (I didn't benchmark though, so it's just a gut feeling). An array_walk solution seems lighter.
If you want to keep key order from $fields, you could try this: (if key not exists in $array, then the value for that key will be null.)
$result = array_flip($fields);
array_walk($result, function(&$item, $key, $array) {
$item = isset($array[$key]) ? $array[$key] : null;
}, $array);
var_dump($result);
I will consider that you can't change the input (neither $array or $fields).
This can be achieved if you have an array that uses as keys the values from $fields. After that you can merge the two (with $fields being the first parameter) and remove the extra elements.
Considering that you can't change $fields, I will create it:
$tmp = array_combine($fields, range(1, count($fields)));
$result = array_merge($tmp, $array);
$result = array_splice($result, 0, count($fields));
The full working sample (with some comments) can be found here: http://codepad.org/H0CDN7ok
My attempt:
array_replace(
array_fill_keys($fields, false),
array_intersect_key($array, # Keys in array, without order
array_flip($fields))));
It was easy to get the keys in the same order as $array. Then to get them in the proper order, I built an array with keys equal to $fields. Array_replace did the rest.
The solution is "stable" in that missing keys in $array will be replaced with FALSE and can thus be filtered out if need be.
array_flip walks the field array of size N once, array_intersect walks M time a N sized array, array_fill_keys costs N and the final array_replace is, I believe, N^2.
So total cost is M*N^5.
Walking the smallest array and picking the values from the large one is O(M^2*N^2), so for large values of N I suspect that the PHP solution might prove faster. This doesn't enter keys which are not in the data array.
$answer = array();
foreach($fields as $fld) // N-sized cycle
if (isset($array[$fld])) // Cost M
$answer[$fld] = // Assignment is N*1/2
$array[$fld]; // Getting value is another M
(some time and much puzzlement later)
I ran a check and I think I must be making some silly mistake, for the times I'm getting are totally nonsensical. Admittedly I'm using a very short $fields array, so I'd expect skewed results, but not this skewed. Unless $answer[$fld] is calculated with some REALLY clever hash trick, whereby the true cost of the interpreted solution is not O(M^2*N^2) but O(K*N^2) with K small.
If anyone wants to play with times or tell me what stupid mistake I might have made, here's the benchmark.
I was of two minds about posting this, because the other obvious explanation is that I made a ridiculous, silly mistake somewhere and I'm going to end up with egg on my face, but, oh, what the hell.
$array = array(
'random' => 1,
'pewpew' => 2,
'temp' => 5,
'xoxo' => 3,
'qweqweqe' => 4,
);
$fields = array('random', 'xoxo', 'temp');
// Let's not print anything just yet, maybe that's what screwing the timer?
$results = '';
$duh = 0;
for ($cycle = 0; $cycle < 10; $cycle++)
{
// Add some more elements to $array.
for ($i = 0; $i < 10000; $i++)
{
$k = uniqid();
$array[$k] = 42;
}
$start = explode(' ', microtime());
// WTF? Do more cycles to average the timing.
for ($j = 0; $j < 10; $j++)
{
// 0 or 1 to switch
if (1)
{
// INTERPRETED ANSWER
$answer = array();
foreach($fields as $fld) // N-sized cycle
if (isset($array[$fld])) // Cost M
$answer[$fld] = // Assignment is N*1/2
$array[$fld]; // Getting value is another M
} else {
// FUNCTION ANSWER
$answer = array_replace(
array_fill_keys($fields, false),
// array_combine($fields, $fields),
array_intersect_key($array, # Keys in array, without order
array_flip($fields)));
}
// USE $answer so to avoid premature optimization?
// You can't be that clever.
$duh += strlen(serialize($answer));
}
$stop = explode(' ', microtime());
// An error in timing? Check via a stupid roundabout.
$int = $stop[1]-$start[1]+1;
$int += ($stop[0]-$start[0]);
$int -= 1;
$elapsed = number_format($int * 1000000, 2);
$results .= "".(5000*$cycle)." = $elapsed us.\n";
}
// I need to get in result:
$wanted = array(
'random' => 1,
'xoxo' => 3,
'temp' => 5,
);
// DID we get the right answer?
print "Wanted:\n"; print_r($wanted);
print "Gotten:\n"; print_r($answer);
print "Results: $results\n$duh -- count of array is " . count($array);
// And yet I have always the same realtime, name of a dog, how can that be?
// I must be doing something REALLY REALLY wrong somewhere.
Easy Way:
$array = array(
'random' => 1,
'pewpew' => 2,
'temp' => 5,
'xoxo' => 3,
'qweqweqe' => 4,
);
$fields = array('random', 'xoxo', 'temp');
$output = array();
foreach ($fields as $value)
if(isset($array[$value]))
$output[$value]=$array[$value];
This is a solution that also handles the case when some $fields are not present as keys in $array:
$flip = array_flip($fields);
$result = array_intersect_key(array_replace($flip, $array), array_intersect_key($flip, $array));
If all $fields are known to be present as keys in $array there is this simpler solution:
$flip = array_flip($fields);
$result = array_intersect_key(array_replace($flip, $array), $flip);
which can be written as a one-liner like this:
$result = array_intersect_key(array_replace($flip=array_flip($fields), $array), $flip);
If some $fields are not keys of $array, but $array contains counts, so that it makes sense to return a 0 count for missing keys, we can replace flip() with array_fill_keys($fields, 0):
$result = array_intersect_key(array_replace($fill=array_fill_keys($fields, 0), $array), $fill);
to which we can apply an array_filter() to filter out the 0s again, if needed. By replacing 0 with false or null we can flag and handle the absence of a key in $array when the values are not counts.
The sad thing is that these solutions, like all others on this page, have to work through all keys of the $array, while any explicit loop would be on $fields. For the time being, it seems that when count($array) is much bigger than count($fields) there is no array-function-based solution as fast as an explicit loop (since they would explicitly construct the result in callback functions, I consider array_walk() and array_reduce() to be explicit loops here).
The problem is that none of the available array_ functions breaks the association between keys and values, and since we would like to loop on $fields, or rather a flipped array thereof, also to conserve its sort order, while keeping the values of $array, we are out of luck.
try:
$result=array();
reset($fields);
while(list($key,$value)=each($fields))
{
if(isset($array[$value])) $result[$value]=$array[$value];
}
This will work and preserve order for you:
$fields = array_flip($fields);
array_merge($fields,array_intersect_key($array, $fields));
$fields = array_keys($fields);
note, you could just call array_flip twice, but the above seemed a bit "cleaner".
The PHP function is called array_diff_key.
Sample code:
$array = array(
'random' => 1,
'pewpew' => 2,
'temp' => 5,
'xoxo' => 3,
'qweqweqe' => 4
);
$fields = array('random', 'xoxo', 'temp');
$result = array_diff_key($fields, $array);
This will produce the desired result.
Demo: http://shaquin.tk/experiments/array1.php
EDIT: If $fields can contain a value that is not a key in $array, then use this code:
$result = array_diff_key($fields, $array);
$result = array_intersect_key(array_flip($fields), $array);
$result = array_flip(array_diff(array_keys($result), $array));
$result = array_replace($result, $array);
$result = array_flip(array_intersect(array_flip($result), $fields));
It may be possible to optimize this a bit, but it works!
Note: I cannot link to an example, since my (hosted) site doesn't have >= PHP 5.3, however, I can link to a similar one: http://shaquin.tk/experiments/array2.php.
I am really new in PHP and need a suggestion about array search.
If I want to search for an element inside a multidimensional array, I can either use array_filter or I can loop through the array and see if an element matching my criteria is present.
I see both suggestion at many places. Which is faster? Below is a sample array.
Array (
[0] => Array (
[id] => 4e288306a74848.46724799
[question] => Which city is capital of New York?
[answers] => Array (
[0] => Array (
[id] => 4e288b637072c6.27436568
[answer] => New York
[question_id_fk] => 4e288306a74848.46724799
[correct] => 0
)
[1] => Array (
[id] => 4e288b63709a24.35955656
[answer] => Albany
[question_id_fk] => 4e288306a74848.46724799
[correct] => 1
)
)
)
)
I am searching like this.
$thisQuestion = array_filter($pollQuestions, function($q) {
return questionId == $q["id"];
});
I know, the question is old, but I disagree with the accepted answer. I was also wondering, if there was a difference between a foreach() loop and the array_filter() function and found the following post:
http://www.levijackson.net/are-array_-functions-faster-than-loops/
Levi Jackson did a nice job and compared the speed of several loop and array_*() functions. According to him a foreach() loop is faster than the array_filter() function. Although it mostly doesn't make such a big difference, it starts to matter, when you have to process a lot of data.
I've made a test script because I was a little skeptical ...how can an internal function be slower than a loop...
But actually it's true. Another interesting result is that php 7.4 is almost 10x faster than 7.2!
You can try yourself
<?php
/*** Results on my machine ***
php 7.2
array_filter: 2.5147440433502
foreach: 0.13733291625977
for i: 0.24090600013733
php 7.4
array_filter: 0.057109117507935
foreach: 0.021071910858154
for i: 0.027867078781128
**/
ini_set('memory_limit', '500M');
$data = range(0, 1000000);
// ARRAY FILTER
$start = microtime(true);
$newData = array_filter($data, function ($item) {
return $item % 2;
});
$end = microtime(true);
echo "array_filter: ";
echo $end - $start . PHP_EOL;
// FOREACH
$start = microtime(true);
$newData = array();
foreach ($data as $item) {
if ($item % 2) {
$newData[] = $item;
}
}
$end = microtime(true);
echo "foreach: ";
echo $end - $start . PHP_EOL;
// FOR
$start = microtime(true);
$newData = array();
$numItems = count($data);
for ($i = 0; $i < $numItems; $i++) {
if ($data[$i] % 2) {
$newData[] = $data[$i];
}
}
$end = microtime(true);
echo "for i: ";
echo $end - $start . PHP_EOL;
I know it's an old question, but I'll give my two cents: for me, using a foreach loop was much faster than using array_filter. Using foreach, it took 1.4 seconds to perform a search by id, and using the filter it took 8.6 seconds.
From my own experience, foreach is faster. I think it has something to do with function call overhead, arguments check, copy to variable return instruction, etc.. When using a basic syntax, the parsed code is more likely to be closer to the compiled/interpreted bytecodes, have better optimization down the core.
The common rule is : anything is simplier, run faster (imply less check, less functionnality, as long as it has all you need)
Array_Filter
Iterates over each value in the input array passing them to the
callback function. If the callback function returns true, the current
value from input is returned into the result array. Array keys are
preserved.
as for me same.
I must check big arrays to see if they are 100% filled with numeric values.
The only way that comes to my mind is foreach and then is_numeric for every value, but is that the fastest way?
assuming your array is one-dimensional and just made up of integers:
return ctype_digit(implode('',$array));
Filter the array using is_numeric. If the size of the result is the same as the original, then all items are numeric:
$array = array( 1, '2', '45' );
if ( count( $array ) === count( array_filter( $array, 'is_numeric' ) ) ) {
// all numeric
}
array_map("is_numeric", array(1,2,"3","hello"))
Array ( [0] => 1 [1] => 1 [2] => 1 [3] => )
I know this question is rather old, but I'm using Andy's approach in a current project of mine and find it to be the most reliable and versatile solution, as it works for all numerical values, negative, positive, and decimal alike.
Here's an average value function I wrote:
$array = [-10,1,2.1,3,4,5.5,6]; // sample numbers
function array_avg($arr) {
if (is_array($arr) && count($arr) === count(array_filter($arr, 'is_numeric'))) {
return array_sum($arr)/count($arr);
} else {
throw new Exception("non-numerical data detected");
}
}
echo array_avg($array); // returns 1.6571428571429
This small function works fine for me
function IsNumericarr($arr){
if(!is_array($arr)){
return false;
}
else{
foreach($arr as $ar){
if(!is_numeric($ar)){
return false;
exit;
}
}
return true;
}
}
Loop is needed
if(array_reduce($array, function($c, $v){return $c & (int)is_numeric($v);}, 1))
The quickest way might be to just assume they're all numerals and continue on with your operation. If your operation fails later on, then you know something isn't a numeral. But if they are all actually numerals... you can't get much faster than O(0)!
I have two arrays. One contains id=>count and the other contains id=>name. I'm trying to produce a single array that is name=>count. Any suggestions on a straightforward way to do this?
I have looked at the Array Functions in the PHP Manual and didn't see anything that stood out as doing what I want, so I'm guessing I'll need a combination of functions, but I'm having trouble coming up with something that's not convoluted.
Something like:
foreach($countA as $id => $count)
{
$newArray[$nameA[$id]] = $count;
}
This does assume that the keys are in correspondence between the two arrays, since your requirements are ambiguous otherwise.
Use array_combine...
$countArray = array(0 => 1, 1 => 5);
$namesArray = array(0 => "Bob", 1 => "Alice");
$assocArray = array_combine($namesArray, $countArray);
Edit: Here is a revised solution for the new requirements expressed in comment #2
$assocArray = array();
foreach($namesArray as $id => $name) {
$assocArray[$name] = (array_key_exists($id, $countArray)) ? $countArray[$id] : 0;
}