I have a long multi-dimensional array, with groups of numbers counting up to 100 ... For example:
$list = (
array('1-4','first'),
array('5-7','next'),
array('8-10','third group'),
array('11-12','another group'),
/* ... keep going up to 100 */
array('94-97','almost done'),
array('98-100','first')
);
I have a randomly generated number, to figure out with line to output:
$num = rand(1,100);
I'd like to know how I would go about getting the # associated inside the array. So, if I rolled a 3, I could get the first array key (1-4), and be able to print out "first" .... or if I rolled a 97, I would get the 2nd-to-last key for "almost done" (94-97). Is there a quick way to do this?
Thanks
You could do something like this if the structure of $list is guaranteed:
$randomRoll = rand(1,100);
array_walk($list, function($ar) use ($randomRoll){
$nums = explode('-', $ar[0]);
(int) $lower = $nums[0];
(int) $upper = $nums[1];
if (($randomRoll >= $lower) && ($randomRoll <= $upper))
{
print($ar[1]);
}
});
You can simplify your lookup array into an associative one since there are no gaps from 1 to 100. In other words, every random number will fall into one of the known groups by determining the greatest number that it is less than or equal to.
Don't use any functional iterators (e.g. array_filter() or array_walk()) because they will unconditionally iterate the entire lookup array. Instead, use foreach() and break as soon as possible -- this is most efficient.
Code: (Demo)
$list = [
4 => 'first', // 1 - 4
7 => 'next', // 5 = 7
10 => 'third group', // 8 - 10
12 => 'another group', // 11, 12
95 => 'huge chunk', // 13 - 95
97 => 'almost done', // 96, 97
100 => 'first' // 98 - 100
];
$num = rand(1, 100);
foreach ($list as $key => $group) {
// echo "$num vs $key\n";
if ($num <= $key) {
break; // don't iterate anymore after finding group
}
}
echo "Num = $num, Group = $group";
I did the following... and it seems to work. If anyone has advice on cleaning up the code, let me know... Thanks...
$dieroll = rand(1,100); // Final Roll
$numarr = array(); $j=0;
foreach ($list as $k => $v) {
$strTr = $v[0];
$nums2arr = explode("-", $strTr);
for($i=$nums2arr[0]; $i<=$nums2arr[1]; $i++) {
$numarr[] = $j;
}
$j++;
}
$roll = $list[$numarr[$dieroll-1]][1];
This solution uses array_filter. The result is again a 2-dimensional array with the elements that fulfill the condition of the filter function.
$filter = function($a) use($find){
list($low,$up) = explode('-',$a[0]);
return $find >= $low AND $find <= $up;
};
$arr = array_filter($list, $filter);
full example:
$list = [
['1-4','first'],
['5-7','next'],
['8-10','third group'],
['11-12','another group'],
/* ... keep going up to 100 */
['94-97','almost done'],
['98-100','first']
];
//for a test is better to use a fix number
$find = 9;
$filter = function($a) use($find){
list($low,$up) = explode('-',$a[0]);
return $find >= $low AND $find <= $up;
};
$arr = array_filter($list, $filter);
echo 'Label: '.reset($arr)[1]."<br>\n";
echo 'Area: '.reset($arr)[0]."<br>\n";
echo 'Array-Key: '.key($arr)."<br>\n";
Output:
Label: third group
Area: 8-10
Array-Key: 2
As of PHP 8.0, solutions with match are also possible for this:
$num = rand(1, 100);
$group = match(true) {
$num <= 4 => 'first',
$num <= 7 => 'next',
$num <= 10 => 'third group',
$num <= 12 => 'another group',
$num <= 95 => 'huge chunk',
$num <= 97 => 'amost done',
$num <= 100 => 'first',
};
echo "Num = $num, Group = $group";
Related
I have an array that I'd like to reorder by every 3rd item. So it's 1st, 4th, 7th and then 2nd, 5th, 8th and 3rd, 6th, 9th.
Input:
$items = ['1','2','3','4','5','6','7','8','9'];
Desired result:
['1','4','7','2','5','8','3','6','9']
Or it can be 3 separate arrays as well. Like this:
['1', '4', '7']
['2', '5', '8']
['3', '6', '9']
I tried array_chunk($items, count($items) / 3) but it just returns the same array divided into 3 equal arrays.
['1', '2', '3']
['4', '5', '6']
['7', '8', '9']
I don't know if I should use array_chunk and then something else to achieve what I want.
UPDATE: It does not have to be exactly 9 items in the input array. it can be shorter or longer, the key point is that it has to take every 3rd item as long as it finds any and so on...
So if there are 10 items:
$items = ['1','2','3','4','5','6','7','8','9','10'];
Desired result:
['1','4','7','10','2','5','8','3','6','9']
The simplest way I can think of is to loop over the list three times:
Start at element 0, skipping by 3, stop when past end of list
Start at element 1, skipping by 3, stop when past end of list
Start at element 2, skipping by 3, stop when past end of list
That can be easily achieved using C-style for loop syntax:
$maxKey = count($items) - 1;
$rearrangedItems = [];
for ( $key=0; $key<=$maxKey; $key+=3 ) {
$rearrangedItems[] = $items[$key];
}
for ( $key=1; $key<=$maxKey; $key+=3 ) {
$rearrangedItems[] = $items[$key];
}
for ( $key=2; $key<=$maxKey; $key+=3 ) {
$rearrangedItems[] = $items[$key];
}
If you needed to generalize to different offsets, you could put the whole thing into an outer loop rather than copying and pasting:
$offset = 3;
$maxKey = count($items) - 1;
$rearrangedItems = [];
for ( $start=0; $start<$offset; $start++ ) {
for ( $key=$start; $key<=$maxKey; $key+=$offset ) {
$rearrangedItems[] = $items[$key];
}
}
Try this
$items = array('1','2','3','4','5','6','7','8','9');
$tmp = [];
for ($i = 0; $i < count($items); $i++) {
$tmp[$i%3][] = $items[$i];
}
$result = array_merge($tmp[0], $tmp[1], $tmp[2]);
print_r($result);
Try this,
$items = array('1','2','3','4','5','6','7','8','9');
$tmp = [];
for ($i = 0; $i < count($items); $i++) {
$tmp[$i%3][] = $items[$i];
}
$result = call_user_func_array('array_merge', $tmp);
dd($result);
Output :
array('1','4','7','2','5','8','3','6','9')
I see this task as nothing more than a sorting task.
Sort each value by it modulus value when divided by 3. If the result is 0, then fallback to 3. Nothing extravagant, just simple, direct, logical, D.R.Y. programming.
Code: (Demo)
array_multisort(
array_map(fn($v) => $v % 3 ?: 3, $items),
$items
);
Pretty straightforward:
$items = array('1','2','3','4','5','6','7','8','9');
$items = array_chunk($items, 3); # Split into arrays of size 3
$items = array_map(null, ...$items); # Transpose the arrays
$items = array_merge(...$items); # Merge the arrays again
print_r($items);
How can I subtract the value of an array if that value is greater than specific data?
For example:
I have an array of menu's position [0,3,10,5,6,9,2,7,1,4,8,11] I want to subtract all values that are greater than selectedDeletedNumbers and stay in their position.
Example: If I delete 3 and 5. I am using checkbox to delete
And the output should be like this.
[0,8,4,7,2,5,1,3,6,9]
Greater than 3 is subtract by 1, and greater than 5 is subtract by 2 since I selected 2 numbers.
HTML
#foreach ($columns as $key => $column)
<div class="checkbox checkbox-danger col-xs-6 el{{ $column->Field }}">
<input type="checkbox" name="CheckboxDelete[]" id="check{{ $column->Field }}" data-column="{{ $key }}" value="{{ $column->Field }}">
<label for="check{{ $column->Field }}"><b style="color: grey">{{ strtoupper($column->Field) }}</b></label>
</div>
#endforeach
Here is my code, but it's only when deleted 1, and not in a position as well.
$searchArr = array_search( $selectedDeletedNumbers, $items);
unset($items[$searchArr]);
$numbers = [];
foreach ($items as $item) {
if ($item > $col[1])
$numbers[] = $item - 1;
}
Thanks in advance.
For clarity and brevity, I'll refer to your input data as:
$array = [0, 3, 10, 5, 6, 9, 2, 7, 1, 4, 8, 11];
$deletes = [3, 5];
There is no sorting or preparation required for either of my snippets.
Output for both of the following are:
array (
0 => 0,
1 => 8,
2 => 4,
3 => 7,
4 => 2,
5 => 5,
6 => 1,
7 => 3,
8 => 6,
9 => 9,
)
Mostly function based approach: (Demo)
foreach (array_diff($array, $deletes) as $value) {
$result[] = array_reduce(
$deletes,
function ($carry, $item) use ($value) {
return $carry - ($value > $item);
},
$value
);
}
var_export($result);
*note: ($value > $item) will evaluated as true or false, when used in arithmetic, true = 1 and false = 0.
Language construct based approach: (Demo)
foreach ($array as $value) {
foreach ($deletes as $delete) {
if ($value == $delete) {
continue 2; // goto next value in outer loop without pushing
}
if ($value > $delete) {
--$value; // decrement value
}
}
$result[] = $value; // push adjusted value into result array
}
var_export($result);
The advantage in this snippet is that there are zero function calls and minimal variables declared.
To delete numbers from array and to subtract a count from the current number in the original array with the number of numbers smaller than it in selectedDeletedNumbers, you can:
Sort the selectedDeletedNumbers array.
Iterate over the original array and use binary search to get the count of numbers smaller than the current number in the original array and subtract it later.
If current number is present in selectedDeletedNumbers, then unset them.
Snippet:
<?php
$arr = [0,3,10,5,6,9,2,7,1,4,8,11];
$selectedDeletedNumbers = [3,5];
sort($selectedDeletedNumbers);
foreach($arr as $index => $val){
$low = 0;$high = count($selectedDeletedNumbers) - 1;
$equal_found = false;
while($low <= $high){
$mid = intval(($low + $high) / 2);
if($selectedDeletedNumbers[$mid] > $val){
$high = $mid - 1;
}else if($selectedDeletedNumbers[$mid] < $val){
$low = $mid + 1;
}else{
$equal_found = true;
unset($arr[$index]);
break;
}
}
if(!$equal_found){
$arr[$index] -= $low;
}
}
print_r($arr);
Demo: http://sandbox.onlinephpfunctions.com/code/ca881fce6a27c216f24a1701190940a87132ab1c
This way, the time complexity will be max(O(m log(m)),O(n log(m))) where n is size of original array and m is the size of selectedDeletedNumbers and space complexity is O(1).
If you want to re-index the numbers sequentially, do an $arr = array_values($arr) in the end.
One more solution is to optimize the checking of what to delete. This code makes sure the delete list is a 0 based array, so in the example it ensures it's
[ 0 => 3, 1 => 5 ]
Then it sorts it descending, but keeps the key, so
[ 1 => 5, 0 => 3 ]
Then when checking the numbers, if the number is greater than 5, it subtracts the key+1, so 1+1 = 2 (sorry for the basic maths). It can then stop and doesn't have to check any more.
$menu = [0,3,10,5,6,9,2,7,1,4,8,11];
$selectedDeletedNumbers = [3,5];
// In case array is not just 0 based index, can remove if it will be
$selectedDeletedNumbers = array_values($selectedDeletedNumbers);
// Sort so highest first, keep key (this is the offset)
arsort($selectedDeletedNumbers);
foreach ( $menu as $key => $menuItem ) {
foreach ( $selectedDeletedNumbers as $deleteIndex => $delete ) {
// Matching, just remove and carry on
if ( $menuItem == $delete ) {
unset($menu[$key]);
break;
}
// Greater than item, so deduct index and move on
if ( $menuItem > $delete ) {
$menu[$key] -= ($deleteIndex+1);
break;
}
}
}
print_r($menu);
If your selected array might come in any order, you can replace the
$selectedDeletedNumbers = array_values($selectedDeletedNumbers);
line with...
sort($selectedDeletedNumbers);
for ($i = 0; $i < count($menuPositions); $i++) {
for ($j = 0; $j < count($selectedDeletedNumbers); $j++) {
if ($menuPositions[$i] == $selectedDeletedNumbers[$j]) {
unset($menuPositions[$i]); // remove the match value from array
$menuPositions = array_values($menuPositions); // reorganize array keeping only the values are not null
$i--; //include the new value of $menuPositions[$i] on interation
break;
}
if ($menuPositions[$i] > $selectedDeletedNumbers[$j] ) {
$menuPositions[$i]--;
}
}
}
I have a code like this:
Lets assume that this arrays has this values:
$arr1 = array();
$arr2 = array();
$result = array();
$arr1[] = array( 'grade' => [1,2,3,4] );
$arr2[] = array( 'grade' => [1,2,3,4] );
foreach($arr1 as $a1){
$set1 = $a1['grade'];
foreach($arr2 as $a2){
$set2 = $a2['grade'];
}
$result[] = array('show_result' => $set1+$set2);
}
foreach{$result as $res){
echo $res['show_result'];
}
The output of the array $res['show_result'] must be:
2, 4, 6, 8
But I get the wrong addition of this arrays. Help will be much appreciated.
As Joni said, your first error is on line 3: ' should be ;
Then, you're not filling arrays like you wanted : array( 'grade' => 1,2,3,4 ); creates an array with first key is 'grade' with value '1', then second key is '0' with value '2' etc...
Your last foreach loop has a syntax error similar to your first error.
See a working correction here
$arr1 = array();
$arr2 = array();
$result = array();
array_push($arr1, 1, 2, 3, 4); //fill array with 4 values (integers)
array_push($arr2, 1, 2, 3, 4); //fill array with 4 values (integers)
//so $arr1 & $arr2 are now a 4 elements arrays
$length = count($arr1); //size of array, here 4
for ($i = 0; $i < $length; $i++) { //loop over arrays
array_push($result, ($arr1[$i] + $arr2[$i])); //fill the results array with sum of the values from the same position
}
var_dump($result);
You have quite a few syntax errors in your code.
Although this solution works, the idea behind using the same counter, $i, to extract a value from both arrays is brittle. For example, you'll get an Undefined offset if the first array has 5 grades instead of 4. If you take a step back and explain your problem in the larger context, perhaps we can provide a better solution. I get the sneaking suspicion you're asking an XY Problem.
http://sandbox.onlinephpfunctions.com/code/bb4f492c183fcde1cf4edd50de7ceebf19fe343a
<?php
$gradeList1 = ['grade' => [1,2,3,4]];
$gradeList2 = ['grade' => [1,2,3,4]];
$result = [];
for ($i = 0; $i < count($gradeList1['grade']); $i++) {
$first = $gradeList1['grade'][$i];
$second = $gradeList2['grade'][$i];
$result['show_result'][] = (int)$first + (int)$second;
}
var_dump($result);
I have an array [4,3,5,5,7,6] and I want to loop through the sorted one and subtract the highest value from the preceding value, then subtract the remainder from the value behind it and so on, then in the end, I need one final value that comes when the loop is completed.
For example
Above array will be sorted like
Array
(
[0] => 3
[1] => 4
[2] => 5
[3] => 5
[4] => 6
[5] => 7
)
Now I want to find the difference between arr[5] and arr[4], the result will be 1, then subtract the result from arr[3] and so on till the loop is completed
This is what I tried but it doesn't seem to work
for ($i = count($a)-1; $i >0; $i--){
echo $result = $a[$i] - $a[$i-1];
echo "<br />";
if($result > 0) {
if($result > $a[$i-2]) {
echo $result = $result - $a[$i-2];
} else {
}
}
I think there is a more simple and fast way to achieve this:
$array = [4, 3, 5, 5, 7, 6];
rsort($array);
$result = $array[0] - $array[1];
for($i = 2, $count = count($array); $i < $count; $i++){
$result = $array[$i] - $result;
}
print($result);
output:
0
Do you wish this way?
$a = [1,1,1,3,1,7];
$result = null;
for ($i = count($a)-1; $i >0; $i--){
if($result == null)
$result = $a[$i-1];
echo $result = $a[$i] - $result;
echo "<br />";
if($result == 0) break;
}
My first answer was wrong, i see you need to discard 2 keys after the first substraction.
This does the job:
<?php
$array = [3,4,5,5,6,7];
$reverse = array_reverse($array);
if (count($reverse) > 1) {
$first = $reverse[0] - $reverse[1];
} else {
//code should stop
}
$result = $first;
for ($i = 2; $i < count($reverse); $i++) {
$result = $reverse[$i] - $result;
}
echo $result;
Ouputs 0, just as in your example. And of course this code still needs check to see if the key of the array does exist while iterating
$numbers = [4,3,5,5,7,6];
sort($numbers);
$numbers = array_reverse($numbers);
$first = array_shift($numbers);
$second = array_shift($numbers);
$result = array_reduce($numbers, function ($carry, $current_item) {
return $current_item - $carry;
}, ($first - $second));
echo $result;
To get the expected output you are seeking you can use rsort to sort descending and start with the highest number. The first 2 elements are subtracted to get the starting value. Loop through the remainder to get your result.
Here's how you can achieve that:
$a = [4, 3, 5, 5, 7, 6]; // Your unsorted array
rsort($a); // Sort array by descending of largest to smallest
$result = $a[0] - $a[1]; // Initial subtraction of first two values
unset($a[0], $a[1]); // Remove from array so it won't loop through
foreach ($a as $_a) { // Loop through remainder and subtract difference
$result = $_a - $result;
}
echo $result; // Show your result
Yields:
0
If you care about reindexing due to the unset you can simply add an extra line after that:
$a = array_values($a); // Reindexes array starting at 0 if you desire
I have an array. I'd like to get the three highest values of the array, but also remember which part of the array it was in.
For example, if my array is [12,3,7,19,24], my result should be values 24,19,12, at locations 4, 0, 3.
How do I do that? The first part is easy. Getting the locations is difficult.
Secondly, I'd like to also use the top three OR top number after three, if some are tied. So, for example, if I have [18,18,17,17,4], I'd like to display 18, 18, 17, and 17, at location 0,1,2,3.
Does that make sense? Is there an easy way to do that?
Wouldn't you be there using asort()?
For example:
<?php
$list = [4,18,18,17,17];
// Sort maintaining indexes.
asort($list);
// Slice the first 3 elements from the array.
$top3 = array_slice($list, -3, null, true);
// Results in: [ 1 => 18, 2 => 18, 3 => 17 ]
Or you can use arsort
function getMyTop($list, $offset, $top) {
arsort($list);
return array_slice($list, $offset, $top, true);
}
$myTop = getMyTop($list, 0, 3);
$myNextTop = getMyTop($list, 3, 4);
This is what you need!
<?php
$array = array(12,3,7,19,24);
$array_processed = array();
$highest_index = 0;
while($highest_index < 3)
{
$max = max($array);
$index = array_search($max,$array);
$array_processed[$index] = $max;
unset($array[$index]);
$highest_index++;
}
print_r($array_processed);
?>
You will get Index as well as the value! You just have to define how many top values you want! Let me know if it's what you want!
function top_three_positions($array){
// Sort the array from max to min
arsort($array);
// Unset everything in sorted array after the first three elements
$count = 0;
foreach($array as $key => $ar){
if($count > 2){
unset($array[$key]);
}
$count++;
}
// Return array with top 3 values with their indexes preserved.
return $array;
}
You can use a loop to determine how many elements your top-three-with-ties will have, after applying arsort:
function getTop($arr, $num = 3) {
arsort($arr);
foreach(array_values($arr) as $i => $v) {
if ($i >= $num && $v !== $prev) return array_slice($arr, 0, $i, true);
$prev = $v;
}
return $arr;
}
// Sample input
$arr = [4,18,17,6,17,18,9];
$top = getTop($arr, 3);
print_r($top); // [5 => 18, 1 => 18, 4 => 17, 2 => 17]
try this:
public function getTopSortedThree(array $data, $n = 3, $asc = true)
{
if ($asc) {
uasort($data, function ($a, $b) { return $a>$b;});
} else {
uasort($data, function ($a, $b) { return $a<$b;});
}
$count = 0;
$result = [];
foreach ($data as $key => $value) {
$result[] = $data[$key];
$count++;
if ($count >= $n){
break;
}
}
return $result;
}
Send false for desc order and nothing for asc order
Send $n with number of top values you want.
This functionality doesn't losing keys.
This task merely calls for a descending sort, retention of the top three values, and in the case of values after the third-positioned value being equal to the third value, retain these as well.
After calling rsort(), call a for() loop starting from the fourth element ([3]). If the current value is not equal to the value in the third position, stop iterating, and isolate the elements from the front of the array to the previous iteration's index. Done.
p.s. If the input array has 3 or fewer elements, the for() loop is never entered and the whole (short) array avoids truncation after being sorted.
Code: (Demo)
$array = [18, 17, 4, 18, 17, 16, 17];
rsort($array);
for ($i = 3, $count = count($array); $i < $count; ++$i) {
if ($array[2] != $array[$i]) {
$array = array_slice($array, 0, $i);
break;
}
}
var_export($array);
Because the loop purely finds the appropriate finishing point of the array ($i), this could also be compacted to: (Demo)
rsort($array);
for ($i = 3, $count = count($array); $i < $count && $array[2] === $array[$i]; ++$i);
var_export(array_slice($array, 0, $i));
Or slightly reduced further to: (Demo)
rsort($array);
for ($i = 3; isset($array[2], $array[$i]) && $array[2] === $array[$i]; ++$i);
var_export(array_slice($array, 0, $i));
Output:
array (
0 => 18,
1 => 18,
2 => 17,
3 => 17,
4 => 17,
)