I have a 2D array in the format:
[
[1, 23, 20],
[2, 45, 30],
[4, 63, 40],
...
]
I am trying to search the array and return elements [0] and [1] from the row where element [1] is lowest. I have the following code to return the lowest value in [1] but I'm not sure how to get element [0] as well.
$min = PHP_INT_MAX;
foreach ($test as $i) {
$min = min($min, $i[1]);
}
In the above example I would return [1, 23]
Thanks,
You should use usort for this:
usort($test, function($a, $b) {
return $a[1] - $b[1];
});
$min = $test[0];
Note that this uses anonymous functions, which were introduced in PHP 5.3. In previous versions, you need to use named functions and pass the name as a string to usort.
You could use array_reduce:
$array = array(
array(1, 23, 20),
array(2, 45, 63),
array(4, 63, 40),
);
$callback = function ($a1, $a2) {
if ($a1[1] >= $a2[1]) {
return $a2;
} else {
return $a1;
}
}
$min = array_reduce($array, $callback, array(0, PHP_INT_MAX, 0));
var_dump($min); // array(1, 23, 20);
Now, this will likely have issues if you have multiple elements with identical [1] elements... But it transparently handles the case where the array is empty. And in general, all you need to do is do your comparison in the callback function for all "filtering" type problems where you can abstract the filtering to a comparison of 2 elements. So you do string comparison, etc to determine which of the 2 is better...
And it should be more efficient than a sort, since it only requires a single pass over the array (It's O(n) whereas sorting is O(n log n) and at worst O(n^2))...
This will do the job.
$min0 = PHP_INT_MAX;
$min1 = PHP_INT_MAX;
foreach ($test as $i) {
if($i[1] < $min1)
{
$min0 = $i[0];
$min1 = $i[1];
}
}
This will result in $min0 == 1 and $min1 == 23
You'll need to introduce some basic logic:
$result = array(0, PHP_INT_MAX);
foreach($test as $i)
if($i < $result[1])
$result = array($i[0], $i[1]);
You can use a simple min (O(n)) loop like you did, and array_slice to assign the first two indexes when a lowest is found :
$min = PHP_INT_MAX;
$arrMin = array();
foreach ($test as $i) {
if ($i[1] < $min) {
$min = $i[1];
$arrMin = array_slice($i, 0, 2);
}
}
you should just grab the array key of the smallest number:
$min = PHP_INT_MAX;
$minkey = 0;
foreach ($test as $key => $i) {
if($min > $i[1]) {
$min = $i[1];
$minkey = $key;
}
}
then, you can just access the whole thing with $test[$minkey]
Related
I have array with positive int values like [4, 1, 75, 52, 5, 24]. I need to find two values with minimal difference. Also, I need the original keys of those two. So, I sorted the array with asort() to keep the keys. Now when I iterate I have a problem - I can't use $key + 1 to point to next element and using next() and prev() makes it difficult to get the keys (once you use next or prev pointer is moved):
for ($i = 0; $i < count($sorted)-1; $i++) {
if (current($sorted) - next($sorted) < $min) {
//echo prev($sorted) - next($sorted) . '<br>';
}
}
What would you do?
(Feel free to alter array in any other form if that makes this easier - asort is not necessary)
If I need to explain one more time: I have a problem with keys. Finding the closest values is not a problem.
I completely revamped your snippet. You can take whatever you want from below snippet,
$array = [4, 1, 5, 52, 75, 52, 24];
function difference($arr)
{
$n = count($arr);
// Initialize difference
// as infinite
$diff = PHP_INT_MAX;
// Find the min diff by comparing
// difference of all possible
// pairs in given array
$two_values = [];
for ($i = 0; $i < $n - 1; $i++) {
for ($j = $i + 1; $j < $n; $j++) {
if (abs($arr[$i] - $arr[$j]) < $diff) {
$diff = abs($arr[$i] - $arr[$j]);
$two_values['values'] = [$arr[$i], $arr[$j]];
$two_values['keys'] = [$i, $j];
$two_values['diff'] = $diff;
}
}
}
// Return min diff
return $two_values;
}
print_r(difference($array));
Demo.
Please let me know if something is not getting.
I found my own way to do this using additional array with keys and array_multisort()
$numbers = [4, 1, 75, 1, 52, 5, 52, 24, 52];
$ar = [];
$mins = [];
$min = PHP_INT_MAX;
foreach ($numbers as $key => $number) {
$ar[] = ['key' => $key, 'number' => $number];
}
array_multisort(array_column($ar, 'number'), SORT_DESC, array_column($ar, 'key'), SORT_DESC, $ar );
foreach ($ar as $key => $value) {
if (!isset($ar[$key + 1])) break;
if ($value['number'] - $ar[$key + 1]['number'] <= $min) {
$min = $value['number'] - $ar[$key + 1]['number'];
$mins = [$ar[$key + 1], $ar[$key]];
}
}
I have an array of integers:
$intArr = [2, 4, 8, 16];
Given an $inputInt of 8, I need the biggest integer match from that array, which should return 8 of course.
The following function needs modification:
function getBiggestMatch($intputInt) {
$intArr = [2, 4, 8, 16];
foreach($intArr as $key => $value) {
if($key < $intputInt) {
$biggestMatch = $value;
}
}
return $biggestMatch;
}
$biggestMatch = getBiggestMatch(8); // should return 8, but returns 2 now
This function will return 2, because that is the first time that $key < $intInput. The desired result needs to be 8 from $intArr.
This should work for you:
First of all, just to make sure, we reindex the array and sort it with array_values() and sort().
After this we loop through the array with a while loop and always check if the next key exists and the current key => element is smaller than the input $number:
Check if next element exists Check that current array value is smaller than the input
┌─────────┴───────┐ ┌─────────┴────────┐
while(isset($arr[$key+1]) && $arr[$key] < $number)
$key++; //→ Go to next key, which must exist
Now after the while loop the $key => value can point to either a value which is the same as the input or higher. Now we just check the following:
value == input ($arr[$key] == $number) => return current value (return $arr[$key];)
distance between current value to input ($arr[$key] - $number) is smaller (<) than the distance between the last value to the input ($number - $arr[$key-1]) => return current value (return $arr[$key];)
else => return last value (return $arr[$key-1];)
Code:
<?php
function getNearestMatch($number) {
$arr = array_values([2, 4, 8, 16, 23]);
sort($arr);
$key = 0;
while(isset($arr[$key+1]) && $arr[$key] < $number)
$key++;
if($arr[$key] == $number || !isset($arr[$key-1]) || $arr[$key] - $number < $number - $arr[$key-1])
return $arr[$key];
else
return $arr[$key-1];
}
//To test it we pass some values to the function
foreach([0, 2, 3, 4, 8, 12, 22, 23, 24] as $v)
echo getNearestMatch($v) . PHP_EOL;
?>
output:
2
2
2
4
8
8
23
23
23
Compare the $value, not the $key. This is returning 8.
<?php
function getBiggestMatch($intputInt) {
$intArr = [2, 4, 8, 16];
foreach($intArr as $key => $value) {
if($value <= $intputInt) {
$biggestMatch = $value;
}
}
return $biggestMatch;
}
echo getBiggestMatch(8); // should return 8, but returns 2 now
?>
You need to do double check because if your array does not have an order the last minor value will return:
function getBiggestMatch($intputInt) {
$intArr = [4, 8, 2, 16];
$maxInt = false;
foreach($intArr as $key => $value) {
if($intputInt >= $value && $maxInt < $value) $maxInt = $value;
}
return $maxInt;
}
echo getBiggestMatch(12);
ideone code
EDIT
Following the #Rizier123's recommendation, the function needs to find the max closest value (I had not understood very well the original question), this could be a possible solution:
function getBiggestMatch($intputInt) {
$intArr = [4, 8, 2, 16];
$maxInt = false;
$diference = 0;
foreach($intArr as $key => $value) {
if( $diference == 0 || ($diference > 0 && abs($intputInt - $value) < $diference) ) {
$maxInt = $value;
$diference = abs($intputInt - $value);
}
}
return $maxInt;
}
echo getBiggestMatch(15);
ideone code
I was trying to create a PHP function that multiplies the values/content of the array by a given argument.
Modify this function so that you can pass an additional argument to this function.
The function should multiply each value in the array by this additional argument
(call this additional argument 'factor' inside the function).
For example say $A = array(2,4,10,16). When you say
$B = multiply($A, 5);
var_dump($B);
this should dump B which contains [10, 20, 50, 80 ]
Here's my code so far:
$A = array(2, 4, 10, 16);
function multiply($array, $factor){
foreach ($array as $key => $value) {
echo $value = $value * $factor;
}
}
$B = multiply($A, 6);
var_dump($B);
Any idea? Thanks!
Your function is not right, It has to return that array and not echo some values.
function multiply($array, $factor)
{
foreach ($array as $key => $value)
{
$array[$key]=$value*$factor;
}
return $array;
}
Rest is fine.
Fiddle
You can even do this with array_map
$A = array(2, 4, 10, 16);
print_r(array_map(function($number){return $number * 6;}, $A));
Fiddle
A simpler solution where you don't have to iterate over the array yourself but instead use php native functions (and a closure):
function multiply($array, $factor) {
return array_map(function ($x) {return $x * $factor}, $array);
}
$myArray = [2, 3, 5];
$factor = 3;
array_walk($myArray, function(&$v) use($factor) {$v *= $factor;});
// $myArray = [6, 9, 15];
or like this if no $factor variable requared
array_walk($myArray, function(&$v) {$v *= 3;});
$A = array(2, 4, 10, 16);
function multiply($array, $factor){
foreach ($array as $key => $value) {
$val[]=$value*$factor;
}
return $val;
}
$B = multiply($A, 6);
var_dump($B);
I want to merge / mix two arrays together, but I want it to be randomly "mixed" however NOT shuffled. For example:
$first = [1, 2, 3, 4];
$second = [10, 20, 30, 40];
Possible desired "mixes" are:
[1, 10, 20, 30, 2, 40, 3, 4]
[10, 1, 2, 20, 30, 3, 4, 40]
[1, 2, 3, 10, 20, 4, 30, 40]
Note that if we pluck the values back out we would still have the original orders of:
1, 2, 3, 4
10, 20, 30, 40
Well, there's (yet another) one possible approach:
$arr = call_user_func_array('array_merge', array_map(null, $first, $second));
print_r($arr); // [1, 10, 2, 20, 3, 30, 4, 40];
Demo. That's apparently deterministic; for random ordering each pair should be shuffled additionally. For example:
function zipShuffle($first, $second) {
return call_user_func_array('array_merge', array_map(function($a, $b) {
return mt_rand(0, 1) ? [$a, $b] : [$b, $a];
}, $first, $second));
}
... but that obviously won't be able to churn out something like [1,2,3,10,20...]. If this is required, here's another solution:
function orderedShuffle($first, $second) {
$results = [];
$stor = [$first, $second];
$i = mt_rand(0, 1);
// switching between arrays on the fly
while (list(, $v) = each($stor[$i])) {
$results[] = $v;
$i = mt_rand(0, 1);
}
// one array is gone, now we need to add all the elements
// of the other one (as its counter left where it was)
$i = 1 - $i;
while (list(, $v) = each($stor[$i])) {
$results[] = $v;
}
return $results;
}
Demo. The last function is actually quite easy to extend for as many arrays as required.
Can you try this,
<?php
$first = array(1,2,3,4);
$second = array(10,20,30,40);
$arrayMixed=array();
$firstReverse=array_reverse($first);
$secondReverse=array_reverse($second);
$firstReverseCount = count($firstReverse);
$secondReverseCount = count($secondReverse);
foreach($firstReverse as $key=>$val) {
if ($firstReverseCount>0) {
array_push($arrayMixed, array_pop($firstReverse));
if ($secondReverseCount>0) {
array_push($arrayMixed, array_pop($secondReverse));
}
}
}
$ArrayMixeddata = array_merge($arrayMixed, $second);
echo "<pre>";
print_r($ArrayMixeddata);
echo "</pre>";
?>
Not quick ways, but them works.
// with permutations
function combineShuffleOrder($first, $second)
{
// combine into one array with alternation
$firstLen = count($first);
$secondLen = count($second);
$max = max($firstLen, $secondLen);
$result = array();
for($i=0; $i < $max; $i++) {
if ($i < $firstLen)
$result[] = $first[$i];
if ($i < $secondLen)
$result[] = $second[$i];
}
// "shuffle" with permutation
$len = count($result);
for($i=1; $i<$len; $i++) {
if (rand(1, 3) % 2 == 0) {
$tmp = $result[$i-1];
$result[$i-1] = $result[$i];
$result[$i] = $tmp;
$i++; // skip one exchange
}
}
return $result;
}
// with using "shuffle" in subarray
function combineShuffleOrder2($first, $second)
{
// combine into one array with alternation
$firstLen = count($first);
$secondLen = count($second);
$max = max($firstLen, $secondLen);
$result = array();
for($i=0; $i < $max; $i++) {
$sub = array();
if ($i < $firstLen)
$sub[] = $first[$i];
if ($i < $secondLen)
$sub[] = $second[$i];
shuffle($sub);
$result = array_merge($result, $sub);
}
return $result;
}
// with using "shuffle" in subarray if sources arrays are equals by length
function combineShuffleOrder3($first, $second)
{
$max = count($first);
$result = array();
for($i=0; $i < $max; $i++) {
$sub = array(
$first[$i],
$second[$i]
);
shuffle($sub);
$result = array_merge($result, $sub);
}
return $result;
}
$first = array(1,2,3,4);
$second = array(10,20,30,40);
print_r(combineShuffleOrder($first, $second));
print_r(combineShuffleOrder2($first, $second));
print_r(combineShuffleOrder3($first, $second));
I recommend forming a single array of the two input arrays for simpler toggling. Simply loop and consume one element from a randomly selected array and push that selection into the result array. When the pool of arrays is reduced to a single array, kill the loop and append the remaining elements of the last surviving array onto the result array.
I'll use a pool of four arrays (one which is empty from the beginning) to demonstrate that the snippet is robust enough to handle a variable number of arrays, a variable number of elements in each array, and empty arrays.
Code: (Demo)
$first = [1, 2, 3, 4];
$second = [10, 20, 30, 40];
$third = [];
$fourth = ['a', 'b'];
$pool = [$first, $second, $third, $fourth];
$result = [];
while (count($pool) > 1) {
$pullFrom = array_rand($pool);
if (!$pool[$pullFrom]) {
unset($pool[$pullFrom]);
continue;
}
$result[] = array_shift($pool[$pullFrom]);
}
var_export(array_merge($result, ...$pool));
Alternatively without array_merge() and count() calls, but it makes more iterated calls of array_shift(): (Demo)
$pool = [$first, $second, $third, $fourth];
$result = [];
while ($pool) {
$pullFrom = array_rand($pool);
if (!$pool[$pullFrom]) {
unset($pool[$pullFrom]);
continue;
}
$result[] = array_shift($pool[$pullFrom]);
}
var_export($result);
Loop this script until both arrays are done.
$j = 0;
$i = 0;
$r = rand(0, 1);
if($r == 0) {
$ret .= $array1[$i];
$i++;
} elseif($r == 1) {
$ret .= $array2[$j];
$j++;
}
Of course, you have to handle a few exceptions in this code, but it might be the route.
I am using the range() function to create an array. However, I want the keys to be the same as the value. This is ok when i do range(0, 10) as the index starts from 0, however if i do range(1, 11), the index will still start from 0, so it ends up 0=>1 when i want it to be 1=>1
How can I use range() to create an array where the key is the same as the value?
How about array_combine?
$b = array_combine(range(1,10), range(1,10));
Or you did it this way:
$b = array_slice(range(0,10), 1, NULL, TRUE);
Find the output here: http://codepad.org/gx9QH7ES
There is no out of the box solution for this. You will have to create the array yourself, like so:
$temp = array();
foreach(range(1, 11) as $n) {
$temp[$n] = $n;
}
But, more importantly, why do you need this? You can just use the value itself?
<?php
function createArray($start, $end){
$arr = array();
foreach(range($start, $end) as $number){
$arr[$number] = $number;
}
return $arr;
}
print_r(createArray(1, 10));
?>
See output here: http://codepad.org/Z4lFSyMy
<?php
$array = array();
foreach (range(1,11) as $r)
$array[$r] = $r;
print_r($array);
?>
Create a function to make this:
if (! function_exists('sequence_equal'))
{
function sequence_equal($low, $hight, $step = 1)
{
return array_combine($range = range($low, $hight, $step), $range);
}
}
Using:
print_r(sequence_equal(1, 10, 2));
Output:
array (
1 => 1,
3 => 3,
5 => 5,
7 => 7,
9 => 9,
)
In PHP 5.5 >= you can use Generator to make this:
function sequence_equal($low, $hight, $step = 1)
{
for ($i = $low; $i < $hight; $i += $step) {
yield $i => $i;
}
}