Filter predicate that filters all items after it's encountered a number

495 Views Asked by At

Below I have an array filter predicate that will remove all items when it encounters a number, including the number. I'm worried about the performance of this predicate as it runs the entire filter many more times then it needs to (forgive me I don't know the bigO). This is because if I want the global state of when it's encountered the number within the predicate I have to access the third argument which is the entire array.

Is there another way to do this, and still have a predicate with no variables outside the predicate scope?

const filterNumberOrAfter = (_: any, index: number, array: any[]): boolean => {
  let encounterdNumber = false
  const total = array.filter((path) => {
    if (encounterdNumber) return false
    if (typeof path === 'number') {
      encounterdNumber = true
      return false
    }
    return true
  })
  return Boolean(total[index])
}

console.log(['hello', 'world', 'meow', 2, 'example', 'rest'].filter(filterNumberOrAfter))
// [ "hello", "world", "meow" ]
2

There are 2 best solutions below

0
ThomasReggi On

One way to optimize is by encapsulating the predicate within a function and caching the initial run of the filter in the parent scope that way each iteration of the filter has access to the original filtered array that only ran once.

const filterNumberOrAfter = () => {
  let total: string[] = []
  return (_: any, index: number, array: any[]): boolean => {
    if (index === 0) {
      let encounterdNumber = false
      total = array.filter((path) => {
        if (encounterdNumber) return false
        if (typeof path === 'number') {
          encounterdNumber = true
          return false
        }
        return true
      })
    }
    return Boolean(total[index])
  }
}

console.log(['hello', 'world', 'meow', 2, 'example', 'rest'].filter(filterNumberOrAfter()))
1
ThomasReggi On

The the commenters credit, this is a nice oneliner that is much easier to read.

const getItemsTillNumber = (arr: any[]) => {
  const i = arr.findIndex((item) => typeof item === 'number')
  return arr.slice(0, i === -1 ? arr.length : i)
}