Selecting the lowest value item in a PHP array - php

I have an array called $products.
If I perform a print_r of this array, I get the following output:
Array
(
[0] => Array
(
[0] => 2
[counter] => 2
[1] => Oranges
[item] => Oranges
)
[1] => Array
(
[0] => 3
[counter] => 3
[1] => Bananas
[item] => Bananas
)
[2] => Array
(
[0] => 1
[counter] => 1
[1] => Apples
[item] => Apples
)
[3] => Array
(
[0] => 1
[counter] => 1
[1] => Pears
[item] => Pears
)
)
I want to be able to list the item name which has the lowest 'counter' value, and if there are duplicate lowest counter values (like in the example above, apples and pears), then for only one to be chosen at random (must be randomly chosen).
Is anyone able to help me with this? Unfortunately I've not been able to find a suitable solution so far. Thanks in advance!

I would personally use usort in such a case, so that if two elements have the same counter you can foresee that later.
Besides, you can easily solve that issue from your mysql query, but here is how I would do this:
<?php
$products = array(
array(
"counter" => 2,
"item" => "Oranges"
),
array(
"counter" => 3,
"item" => "Bananas"
),
array(
"counter" => 1,
"item" => "Apples"
),
array(
"counter" => 1,
"item" => "Pears"
)
);
usort($products, function($previous, $next) {
return $previous["counter"] > $next["counter"] ? 1 : -1;
});
echo "Lowest counter is: {$products[0]['item']}";
?>
Explanation:
usort($products, function($previous, $next) {
return $previous["counter"] > $next["counter"] ? 1 : -1;
});
usort takes $products (the array) as the array to fetch and loops two items at a time (previous and next), the return statements check whether the previous element's counter is higher than the next one, if so, it puts it after it, else before it.
In the end, you will have the array elements sorted from the lowest counter one to the highest one.
I would suggest you, also, to later get the ones that have the same counter value if, such in this case, you have two elements which both are low, like this:
$products = array(
array(
"counter" => 2,
"item" => "Oranges"
),
array(
"counter" => 3,
"item" => "Bananas"
),
array(
"counter" => 1,
"item" => "Apples"
),
array(
"counter" => 1,
"item" => "Pears"
)
);
usort($products, function($previous, $next) {
return $previous["counter"] > $next["counter"] ? 1 : -1;
});
$lowest = $products[0]["counter"];
$filtered = array_filter($products, function($element) use ($lowest) {
return $element["counter"] === $lowest;
});
echo $filtered[array_rand($filtered, 1)]["item"];
working code:
https://3v4l.org/YPAeh

How about simplifying with the use of usort?
function compare_products($product1, $product2){
if($product1['counter'] == $product2['counter']){
return rand(0,1) ? 1 : -1//if it is the same then it is random
}
return $product1['counter'] > $product2['counter'];// if not it sorts the array
}
$products = ...;//your array
usort($products, "compare_products");
Then to get the lowest just do
echo $products[0];
Working example here (Idone) I simplified your array for the example

<?php
$products;//What ever you have assigned to this...
$size = count($products); // size of products array
$tmp = PHP_INT_MAX; // Set this to the highest possible counter value.
for($i = 0; $i < $size; $i++)
{
//If a new lowest counter has been found empty array and append this value
if($products[$i]["counter"] < $tmp)
{
$lowest = array();
$lowest[0] = $products[$i]["item"];
$tmp = $products[$i]["counter"];
}
//If counter is equally low append value
if($products[$i]["counter"] == $tmp)
{
$lowest[] = $products[$i]["item"];
}
}
echo array_rand($lowest);
?>
EDIT: Updated the code.
EDIT2: Addes Explanation below the code
This code works as follows:
The loop circels through the entire array. Which each step it checks whether the counter is lower then the one previously stored, initially the highest possible int value. If it is, the array with all wanted values is completely reset.
Next the loop checks whether the lowest value is the same as the previously stored "lowest" value. If so the item name is added to the array. In this way syou can collect all item names with the lowest value (one or more item names) in one array. If one smaller value occurs all item names before are not longer interesting and hence can be deleted. Once the loop has finished the function array_rand() outputs one random value, as you want it to. This is outputted as a string but can also be assigned to a variable.
I hope this helps to understand my code.

You want to get random minimum array from $data. For this you should do some following code.
$data[]=array
(
'0' => 2,
'counter' => 2,
'1' => 'Oranges',
'item' => 'Oranges'
);
$data[]=array
(
'0' => 3,
'counter' => 3,
'1' => 'Bananas',
'item' => 'Bananas'
);
$data[]=array
(
'0' => 1,
'counter' => 1,
'1' => 'Apples',
'item' => 'Apples'
);
$data[]=array
(
'0' => 1,
'counter' => 1,
'1' => 'Pears',
'item' => 'Pears'
);
$min=min(array_column($data, 'counter'));
$minarray=array();
foreach($data as $key=>$value) {
if($value['counter']==$min){
array_push($minarray,$value);
}
}
$rand_keys =array_rand($minarray);
echo"<pre>";
print_r($minarray[$rand_keys]);
Try this code on http://phpfiddle.org/ for online test.
I hope you will get solution.

This will take your $products and loop over each of them. Using the first element to set your currentLowest. If the value is a match it will add it to an array. If something less is found it will replace the currentLowest and remove the array values adding that value to a new one. At the end array_rand will give you a random index that you can use to access the lowValues.
<?php
//products is defined somewhere
$lowValues = array();
//Just to get it started
$currentLowest = $products[0]["counter"];
foreach($products as $product){
if($product["counter"] == $currentLowest){
//When Equal just add it
$lowValues[] = $product["item"];
} elseif($product["counter"] < $currentLowest) {
//When lower set as lowest
$currentLowest = $product["counter"];
//Remove all old values
unset($lowValues);
//Add this to the low values
$lowValues[] = $product["item"];
} else {
//Nothing to do here...
}
}
//array_rand is an index.
echo $lowValues[array_rand($lowest)];
?>

Related

Create array batch based on similar array key

I am trying to create an batch of similar array's based on first key of array.
Here is the input array
$headers = array(
0 => 'order_number',
1 => 'product_sku',
);
$input_array = array (
0 =>
array (
0 => '311000000706',
1 => 'S503053'
),
1 =>
array (
0 => '311000000706',
1 => 'S516135'
),
2 =>
array (
0 => '311000000703',
1 => 'S526587'
),
3 =>
array (
0 => '311000000703',
1 => 'S526587'
),
);
I am trying to create an seperate array if it has same order id like if two array has same order id = "311000000706"
then desired output will be
$desired_output = array (
0 =>
array (
0 => '311000000706',
1 => 'S503053'
),
1 =>
array (
0 => '311000000706',
1 => 'S516135'
));
Here is what i have tried
$prev_order_id_s = null;
$similar_order_ids = array();
foreach ($input_array as $key => $level):
if ($level['0'] == $prev_order_id_s) {
$similar_order_ids[][] = $level;
}
$prev_order_id_s = $level['0'];
endforeach;
but i am not getting desired output, i need some advice on this.
can you please advice some logic on this ?
Here is the compiler with above code - paiza.io/projects/fB-8CfiEuTX1cJXnXAZS3g
Instead of maintaining variables for previous order IDs, I would rather suggest you to take advantage of order ID as an associative key and keep adding rows to it whenever you find a matching order ID. In the end, you could array_values() on the collected data to remove order IDs as keys.
<?php
$set = [];
foreach($input_array as $row){
$set[$row[0]][] = $row;
}
$result = array_values($set);
print_r($result);
Do not use foreach to filter your array. You can use the array_filter function in PHP. Let's assume you want to check the first element of each array (means the zero index) and your desired value for this key is '311000000706'. It transaltes to PHP like this:
$desired_output= array_filter($input_array, function ($value) {
return $value[0] === '311000000706';
});

How to find the median of deepest subarrays of multidimensional array?

I have a four-level multidimensional array. I need to sort in ascending order (ASC) the numeric "leaves" in order to calculate the median of the values.
I tried array_walk_recursive(), array_multisort(), usort(), etc. but was unable to find a working solution.
Here's a schematic of the array:
(
[2017-05-01] => Array
(
[DC] => Array
(
[IT] => Array
(
[0] => 90
[1] => 0
)
[DE] => Array
(
[0] => 18
[1] => 315
[2] => 40
[3] =>
[4] => 69
)
[Other] => Array
(
[0] => 107
[1] => 46
[2] =>
[3] =>
[4] => 27
[5] => 22
)
)
)
)
This will output the deepest subarrays' median values using the input array's structure.
I'm including hard-casting of median values (one or both in a subset) as integers in the event that the value(s) are empty strings. I'll also assume that you will want 0 as the output if a subset is empty.
Code: (Demo)
$array=[
'2017-05-01'=>[
'DC'=>[
'IT'=>[90, 0],
'DE'=>[18, 315, 40, '', 69, 211],
'Other'=>[107, 46, '', '', 27, 22]
]
],
'2017-05-02'=>[
'DC'=>[
'IT'=>[70, 40, 55],
'DE'=>['', 31, 4, '', 9],
'Other'=>[1107, 12, 0, 20, 1, 11, 21]
]
],
'fringe case'=>[
'DC'=>[
'IT'=>[],
'DE'=>['', '', '', 99],
'Other'=>['', 99]
]
]
];
foreach ($array as $k1 => $lv1) {
foreach ($lv1 as $k2 => $lv2) {
foreach ($lv2 as $k3 => $lv3) {
sort($lv3); // order values ASC
$count = sizeof($lv3); // count number of values
$index = floor($count / 2); // get middle index or upper of middle two
if (!$count) { // count is zero
$medians[$k1][$k2][$k3] = 0;
} elseif ($count & 1) { // count is odd
$medians[$k1][$k2][$k3] = (int)$lv3[$index]; // single median
} else { // count is even
$medians[$k1][$k2][$k3] = ((int)$lv3[$index-1] + (int)$lv3[$index]) / 2; // dual median
}
}
}
}
var_export($medians);
Output:
array (
'2017-05-01' =>
array (
'DC' =>
array (
'IT' => 45,
'DE' => 54.5,
'Other' => 24.5,
),
),
'2017-05-02' =>
array (
'DC' =>
array (
'IT' => 55,
'DE' => 4,
'Other' => 12,
),
),
'fringe case' =>
array (
'DC' =>
array (
'IT' => 0,
'DE' => 0,
'Other' => 49.5,
),
),
)
*for the record, $count & 1 is a bitwise comparison that determines if the value is odd without performing arithmetic (and is the most efficient way of performing this check within php).
*also, if you wanted to simply overwrite the values of the input array, you could modify by reference by writing & before $lv1, $lv2, and $lv3 in the foreach declarations then save the median value to $lv3. Demo The benefit in doing so removes key declarations and making your code more brief.
As it turns out, there is a way to do what the OP seeks using a combination of usort() and array_walk(), each of which takes a callback, as follows:
<?php
// median code:
//http://www.mdj.us/web-development/php-programming/calculating-the-median-average-values-of-an-array-with-php/
function calculate_median($arr) {
sort($arr);
$count = count($arr); //total numbers in array
$middleval = floor(($count-1)/2); // find the middle value, or the lowest middle value
if($count % 2) { // odd number, middle is the median
$median = $arr[$middleval];
} else { // even number, calculate avg of 2 medians
$low = $arr[$middleval];
$high = $arr[$middleval+1];
$median = (($low+$high)/2);
}
return $median;
}
$a = [];
$a["2017-05-01"] = ["DC"];
$a["2017-05-01"]["DC"]["IT"] = [90,0];
$a["2017-05-01"]["DC"]["DE"] = [18,315,40,"",69];
$a["2017-05-01"]["DC"]["Other"] = [107,46,"","",27,22];
function sort_by_order ($a, $b)
{
if ($a == "") $a = 0;
if ($b == "") $b = 0;
return $a - $b;
}
function test($item,$key){
echo $key," ";
if (is_array($item)) {
echo array_keys($item)[1],"\n";
$popped = array_pop($item);
foreach ($popped as $key => $arr) {
usort($arr, 'sort_by_order');
echo "Median ($key): ",calculate_median( $arr ),"\n";
}
}
}
array_walk($a, 'test');
See demo here. Also, see this example based on the OP's sandbox.
Although the OP's code does not show the array keys as quoted, beware they should be in the actual code, otherwise PHP will do math with 2017-05-01 and you'll see a key of 2011. Interesting read here about usort.
The median code I extracted from here.
Interestingly, the conventional wisdom about sorting numbers to determine the median is not necessarily the only way to obtain that result. Apparently, it can also be done and perhaps more efficiently by finding a pivot number and dividing the series of numbers into three parts (see this response).

PHP: take out duplicate digits from an array then print them out

I'm probably [super]overthinking this. I'm trying to analyze an array with values like [1,9], [4,6] [5,5], [6,4], [9,1] and duplicate digits (I'm having a super brain fart and can't even remember the term for numbers like this) remove (the last two) so that only [1,9], [4,6] [5,5] are printed.
I was thinking that turning this array into a string and using preg_match, but I'm pretty sure this wouldn't work even if I had the correct regex.
If you have an array of pairs like this:
$x = array(
array(1,9),
array(4,6),
array(5,5),
array(6,4),
array(9,1)
);
Here is one way to get the unique pairs:
foreach ($x as $pair) {
sort($pair);
$unique_pairs[implode(',', $pair)] = $pair;
}
This uses string representations of each sorted pair as keys in a new array, so the result will have distinct values by definition.
As far as the printing them out part of your question, once you have the unique values you can loop over them and print them out in whichever format you like, for example:
foreach ($unique_pairs as $pair) { vprintf("[%d,%d]<br>", $pair); }
It looks like elements are distributed symmetrically.
We can cut the array in two halves and get only the first half with array_slice():
$array = array(
array(1,9),
array(4,6),
array(5,5),
array(6,4),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 4
[1] => 6
)
[2] => Array(
[0] => 5
[1] => 5
)
)
Demo at Codepad.
ceil() is used to round the number up to the next highest integer if there is an even number of items in the array. Example: if there is 3 items in the array, 5 / 2 will return 2.5, we want 3 items so we use ceil(2.5) which gives 3.
Example with 3 items:
$array = array(
array(1,9),
array(5,5),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 5
[1] => 5
)
)
Example with 4 items:
$array = array(
array(1,9),
array(7,7),
array(7,7),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 7
[1] => 7
)
)
If I'm correct in understanding what you are trying to do, you want to remove the final 2 elements from the array?
There is a function in PHP called array_pop that removes the final element from the array.
$array = array_pop($array);
So if you run this twice, you will remove the final 2 elements from the array.
This is how I'd do it (and I hope I am not overthinking this :))
$stringArray = array();
$stringArray[] = '1,9';
$stringArray[] = '4,6';
$stringArray[] = '5,5';
$stringArray[] = '6,4';
$stringArray[] = '9,1';
foreach($stringArray as &$numString) {
$numString = explode(',', $numString);
usort($numString, function($a, $b) {return $a - $b;});
$numString = implode(',', $numString);
}
$a = array_unique($a);
print_r($a);
You basically explode every element into a subarray, sort it and then implode it back. After calling the array_unique, you're left with unique values in the array.
The output would be
Array
(
[0] => 1,9
[1] => 4,6
[2] => 5,5
)
The result you suggest treats [a,b] as equivalent to [b,a] which makes the problem a lot more complex. The code below gives the result you asked for, but without really understanding what the problem is that you are trying to fix and whether [1,9] is equivalent to [9,1] in the solution:
$a=array(array(1,9),array(4,6),...
$dup=array();
for ($i=0; $i<count($a) -1; $i++) {
for ($j=$i+1; $j<count($a); $j++) {
if (($a[$i][0]==$a[$j[0] && $a[$i][1]==$a[$j[1])
|| ($a[$i][0]==$a[$j[1] && $a[$i][1]==$a[$j[0])) {
$dup[]=$j;
}
}
}
foreach ($dup as $i) {
unset($a[$i]);
}
So I'm actually going to assume your question to have a different meaning than everyone else did. I believe what you're asking is:
How do you filter out array items where a reverse of the item has already been used?
<?php
// The example set you gave
$numberSets = [[1, 9], [4, 6], [5, 5], [6, 4], [9, 1]];
// Initialize an empty array to keep track of what we've seen
$keys = [];
// We use array filter to get rid of items we don't want
// (Notice that we use & on $keys, so that we can update the variable in the global scope)
$numberSets = array_filter($numberSets, function($set) use(&$keys) {
// Reverse the array
$set = array_reverse($set);
// Create a string of the items
$key = implode('', $set);
// Get the reverse of the numbers
$reversedKey = strrev($key);
// If the palindrome of our string was used, return false to filter
if (isset($keys[$reversedKey])) {
return false;
}
// Set the key so it's not used again
// Since $keys is being passed by reference it is updated in global scope
$keys[$key] = true;
// Return true to NOT filter this item, since it or it's reverse were not matched
return true;
});
var_dump($numberSets);

Return arrays from foreach and sum their values

In a category overview, I need to sum all items that were studied in all the subcategories.
I have a function countitemsinsubcat() inside a foreach() that returns an array '$value' for each subcategory ($id_cat).
foreach ($subcategory as $row) {
$value =& countitemsinsubcat($id_cat);
$all_values_found [] = $value;
}
So these is $all_values_found for a category that has 2 subcategories:
Array (
[0] => Array(
[Istudied] => 0
[Itotal] => 1
)
[1] => Array (
[Istudied] => 1
[Itotal] => 4
)
)
In a category overview, I want to sum the values of the arrays of each subcategory, and get a 'total' array, like this:
Array
(
[Istudied] => 1
[Itotal] => 5
)
Any suggestions on how do this?
Have a look at this code snippet [PHP]:
//The magic happens here:
function concatit($v, $w)
{
$v[Istudied] += $w[Istudied];
$v[Itotal] += $w[Itotal];
return $v;
}
//Declaring the array.
$a = array (array(
Istudied => 0,
Itotal => 1
),array (
Istudied => 1,
Itotal => 4
)
);
//Making a call to the 'concatit' function declared above from within array_reduce.
$d = array_reduce($a, "concatit");
//Now $d contains the array as you wanted it.
echo $d[Istudied].' '.$d[Itotal];
Let me know if you need further clarifications! Enjoy!

Break array into chunks and prepend each row with a static element

I have the following array:
$list = array('item1','item2','item3','item4','item5','item6');
i need to take this array and break it into smaller arrays within 1 array for the csv. Each small array needs to have '999999' at index 0, followed by the next two items in the $list array. So the final result would be like this:
$newList = array(
array(999999, "item1" , "item2"),
array(999999, "item3" , "item4"),
array(999999, "item5" , "item6")
);
The original list array may contain up to 100 values at sometimes. What is the best way to achieve this?
Here's a different way of doing it, please see the comment as to where you place your additional elements to be prepended (you could add a second array into the merge at the end to add elements after the 2 list items too!)
See here for working example: http://codepad.org/hucpT5Yo
<?php
$list = array('item1','item2','item3','item4','item5','item6');
$list_chunks = array_chunk($list,2);
$list_chunks2 = array_map(
create_function(
'$x',
'return array_merge(array(
/* This array is prepended to the second,
add as many elements as you like */
999999,
"another element to add",
483274832
),
$x);'),
$list_chunks);
print_r($list_chunks2);
?>
$idx = 0;
$newarray = array();
$temparray = array('999999');
foreach($oldarray as $value) {
$temparray[] = $value;
if ((++$idx % 2)) == 0) {
$newarray[] = $temparray;
$temparray = array('999999');
}
}
Break your flat array into an array 2-element rows with array_chunk(), then call array_merge() on each row with the static value 999999 nested in an array and nominated as the first argument.
The modern syntax for the chunk-n-merge technique demonstrated by #Lee looks like this:
Code: (Demo)
$list = ['item1','item2','item3','item4','item5','item6'];
var_export(
array_map(fn($row) => array_merge([999999], $row), array_chunk($list, 2))
);
Output:
array (
0 =>
array (
0 => 999999,
1 => 'item1',
2 => 'item2',
),
1 =>
array (
0 => 999999,
1 => 'item3',
2 => 'item4',
),
2 =>
array (
0 => 999999,
1 => 'item5',
2 => 'item6',
),
)

Categories