7

So far I tried this but it returns unfiltered array:

function filterRangeInPlace(array, min, max) {
  array = array.filter(item => (item >= min && item <= max));
  console.log(array);
}

let arr = [5, 3, 8, 1];

filterRangeInPlace(arr, 1, 4);

console.log(arr);

6
  • 1
    .filter() always creates a new array. Your code doesn't work because the assignment to array in the function is an assignment to the parameter and JavaScript is a pass-by-value language. Commented Aug 21, 2019 at 19:01
  • To actually achieve what the question title requests you'd have to write a function to "smush" the array in place to get rid of filtered-out entries. Commented Aug 21, 2019 at 19:03
  • 1
    Pass Variables by Reference in Javascript Commented Aug 21, 2019 at 19:04
  • The function .filter() is doesn't like a for loop, This function actually apply the filter on array entities and return a new filtered array, It doesn't make changes on a actual array like for loop, So when you need to filter any array then you must need to take a variable which holds filtered array result or just use for loop and remove unsatisfied entries from the array which affects on original array elements Commented Aug 21, 2019 at 19:22
  • @NeelRathod To be fair, a for loop can totally create a new filtered array. Under the hood, I would guess that .filter() uses a loop of some kind. Commented Oct 5, 2023 at 23:26

4 Answers 4

10

If it's actually important to do the filtering in-place without creating another array, you have to go sort-of old school and iterate through the array with two indexes, copying values along the way. Every time you hit an element that fails the filter test, you increment one of the indexes but not the other one. At the end of that, you reset the array .length to the trailing index:

function filterInPlace(array, fn) {
  let from = 0, to = 0;
  while (from < array.length) {
    if (fn(array[from])) {
      array[to] = array[from];
      to++;
    }
    from++;
  }
  array.length = to;
}

This has the advantage of being O(n), with just one pass over the array, while the solutions involving .splice() are O(n2).

To do your "range check", you could write another function to create a filter predicate given a minimum and a maximum value:

function rangePredicate(min, max) {
  return n => n >= min && n <= max;
}

Then you can pass the return value of that to the filter function:

var arr = [1, 2, 3, ... ];
filterInPlace(arr, rangePredicate(0, 10));
Sign up to request clarification or add additional context in comments.

5 Comments

Thank You for Your awesome answer, this is what I looking for - fast and cheap (for memory) way to filter an array.
Please, can You add Your version of fn for completeness of the answer? Thank You
@RobertHovhannisyan Well fn can be any function, just like a function you'd pass to .filter(). If it returns true then the value is included in the final array, otherwise it isn't.
@RobertHovhannisyan answer extended.
Oh, that's great. Now everything is clear. Thank You again!
4

You need to return the new filtered array and assign it to a variable (such as arr itself):

function filterRangeInPlace(array, min, max){
  return array.filter(item => (item >= min && item <= max));
}

let arr = [5, 3, 8, 1];

arr = filterRangeInPlace(arr, 1, 4);

console.log(arr);

2 Comments

This works of course but it doesn't satisfy the "without another array" part of the question.
That was a pretty smart way to filter an array but I agree with @Pointy in this case. But Thank You anyway for Your good answer!
3

Return the value and set it equal to itself.

function filterRangeInPlace(array, min, max) {
  return array.filter(item => (item >= min && item <= max));
}

let arr = [5, 3, 8, 1];

arr = filterRangeInPlace(arr, 1, 4);

console.log(arr);

Or you could of course omit the function entirely with:

let arr = [5, 3, 8, 1];

arr = arr.filter(item => (item >= min && item <= max));

console.log(arr);

2 Comments

That interesting and short answer. Thank You for Your effort!
@RobertHovhannisyan Huh? Is that not exactly what you were trying in your question that didn't work?
3

You can't use .filter() for this, since it returns a new array rather than modifying the original array. You can loop over the array yourself, removing elements that don't match the condition.

You need to do the loop in descending order of index, because removing an element shifts the indexes of all the remaining elements down and you'll end up skipping the next element if you do it in increasing order. This also means you can't use a built-in function like .forEach().

function filterRangeInPlace(array, min, max) {
  for (let i = array.length-1; i >= 0; i--) {
    if (array[i] < min || array[i] > max) {
      array.splice(i, 1);
    }
  }
}

let arr = [5, 3, 8, 1];
filterRangeInPlace(arr, 1, 4);
console.log(arr);

4 Comments

You do if you use array.splice(). I guess you could go forward and decrement i.
But you can't use forEach, it doesn't provide a way to back up.
I'd do what I wrote in my answer because somewhere in my head I'm still a C programmer on a pdp-11.
That was an awesome solution. Thank You very much for Your code!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.