2

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.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Marky
  • 55
  • 1
  • 2
  • 11
  • Oops, sorry forgot to indicate. The numbers that preceded is still the same, `0,1,2` is still the same. – Marky May 15 '20 at 05:39
  • _greater than 5 is subtract by 2 since I selected 2 numbers_ This is ambiguous. Let's take 10 for example. 10 is made 8 since 10 is greater than both 3 and 5. Am I right? – nice_dev May 15 '20 at 06:25
  • @vivek I'll try to reframe the question. There is a collection of integers from 0 to 11. The user will decide to remove one or more of the numbers from the array. Then the numbers in the list must all be "shifted down" so that the lowest number is `0` and there are no gaps in the output array. ...all of this WHILE maintaining the original order of the non-deleted integers. In other words, this is about 1. deletion then 2. gap removal. – mickmackusa May 15 '20 at 06:32
  • @mickmackusa I got a small problem the value of my checkbox is `0: "home, 1" 1: "about, 2"`. Can you help me to get the value number of `[1,2]`. Thanks – Marky May 15 '20 at 06:38
  • There are many ways to do that. https://3v4l.org/Pbatt https://stackoverflow.com/questions/6278296/extract-numbers-from-a-string However, I think it would be better if you refined your checkbox html so that only the number is held in the `value` attribute. – mickmackusa May 15 '20 at 06:41
  • @mickmackusa So basically maintain the order of elements and subtract the number of numbers in `selectedDeletedNumbers` that are smaller than the current number. Am I right? – nice_dev May 15 '20 at 06:44
  • 1
    @viv that sounds accurate. Subtract {the number of elements in the deletion array that are smaller than the currently iterated value} from the currently iterated value. Making sure to remove the current element if its original value was nominated in the deletion array. – mickmackusa May 15 '20 at 06:48
  • What have you tried so far? Where are you stuck? Looks like a pretty good exercise to learn some test-driven development – Nico Haase Jul 09 '20 at 12:44
  • @NicoHaase I got a sample here https://3v4l.org/lX2MP. I stuck on returning the array value, they are combining, it supposed to be not since they are 4 arrays inside the array. – Marky Jul 09 '20 at 12:48
  • Please add all clarification to your question. There's no need to put code to an external site – Nico Haase Jul 09 '20 at 12:49

4 Answers4

2

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.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • 1
    I use your old code with `in_array` it's working perfectly. Thanks you save my day. Cheers – Marky May 15 '20 at 08:08
  • @Marky you now have the privilege to upvote helpful answers. I hope that you will consider upvoting all answers that you found helpful/correct. You received some excellent answers here today. – mickmackusa May 15 '20 at 08:09
  • Hi again @mickmackusa, I need help again. I got a problem when my data array is like this **$array = [[0, 3, 10, 5, 6, 9, 2, 7, 1, 4, 8, 11], [0,1], [0,1], [0,1]];**. Got wrong result. – Marky Jul 09 '20 at 10:06
  • @Marky looks like a very different question and I don't know what output you expect to see from this new input structure. – mickmackusa Jul 09 '20 at 11:47
  • Are you expecting this? `[4, 0, 3, 1, 2, 5]`? Here is an non-elegant extension of my answer: https://3v4l.org/2XdI4 I have worky-work to do tonight, so I cannot play. – mickmackusa Jul 09 '20 at 11:56
  • I have now noticed that you have extended your question via an edit. You mustn't do this. Volunteers are not expected to provide extended support for your project months after helping you. If you have a new issue, then post a new question. @Marky – mickmackusa Jul 09 '20 at 12:04
  • I thought it can support me, sorry my bad. I will just post another. Thanks for your response by the way. – Marky Jul 09 '20 at 12:28
  • This is new post sir @mickmackusa. Thanks in advance https://stackoverflow.com/questions/62814826/how-to-subtract-value-of-array-but-still-in-position – Marky Jul 09 '20 at 12:30
1

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);
Nigel Ren
  • 56,122
  • 11
  • 43
  • 55
  • @mickmackusa depends, if the selected delete array comes from a list of check boxes, I would assume (perhaps this is the mistake) that they will come in numerical order. So if they are 0,11 it works. – Nigel Ren May 15 '20 at 07:01
  • @mickmackusa, that was more in case you ended up with `[1=>3, 4=>5]`, but if you change it as the update, then it should work - thanks for your *help* ;) – Nigel Ren May 15 '20 at 07:05
1

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.

nice_dev
  • 17,053
  • 2
  • 21
  • 35
0
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]--;
            }
        }
    }
Lucas Pace
  • 139
  • 9