简体   繁体   中英

Is there a better way to write redundant filter statements in JavaScript?

I have multiple if statements which I have presented using ternary operator and then for each If a separate filter, This works perfectly fine in React, but the repetition of filter statement makes the code look untidy, id there a way to remove this repetition?

let ar = data;
id === 'A' ? ar = ar.filter(item => item > 4) : Id === 'B' ? ar = ar.filter(item => item > 4 
&& item < 8) : Id === 'C' ? ar = ar.filter(item => item >8 && item < 15) : ar = data;

You can create a function that will return the filter function, like:

function makeFilterFn(id){
  switch (id){
      case 'A':
        return item => item > 4
      case 'B':
        return item => item > 4 && item < 8
      case 'C':
        return item => item > 8 && item < 15
      default:
        return null
  }
}

const filterFn = makeFilterFn(id)

const result = filterFn ? ar.filter(filterFn) : ar

 const ar = [1,2,3,4,5,6,7,8,9,10]; const id = "A"; const filtered = { "A": ar.filter(item => item > 4), "B": ar.filter(item => item > 4 && item < 8), "C": ar.filter(item => item >8 && item < 15) }[id] console.log(filtered)

Let's run through some refactoring and see what we can improve. First I'll split it across multiple lines just so that I can see it easier:

let ar = data;
id === 'A' ?
  ar = ar.filter(item => item > 4) : 
id === 'B' ? 
  ar = ar.filter(item => item > 4 && item < 8) : 
id === 'C' ? 
  ar = ar.filter(item => item > 8 && item < 15) :
//default
ar = data;

First thing I notice is that you are assigning multiple times, but using a ternary, which makes that unneeded. Let's fix that:

let ar = data
ar = (
id === 'A' ?
  ar.filter(item => item > 4) : 
id === 'B' ? 
  ar.filter(item => item > 4 && item < 8) : 
id === 'C' ? 
  ar.filter(item => item > 8 && item < 15) :
//default
data
)

Next, ar.filter is getting called every time (except for the default case), and only the arguments are changing. The default case can be changed to be a filter that returns everything so that it is more consistent, and then we can extract the arguments to a variable (let's call it filter ):

let ar = data
// we are just determining the argument here
let filter = ( 
id === 'A' ?
  item => item > 4 : 
id === 'B' ? 
  item => item > 4 && item < 8 : 
id === 'C' ? 
  item => item > 8 && item < 15 :
// default is to return all items
item => true
)

// now we can use the argument in the function call
ar = ar.filter(filter)

That removes most of the duplication here, but we are comparing to id in the same way multiple times. Like Pavlos mentioned, if you want to pick an option based on a string id, basic objects can let you do that. The problem is we loose our default argument, but we can usually use the || ("or") operator to fix that:

let ar = data

// another benefit of doing it this way is that it is super easy to add 
// new filters now, and you can even pass this in as a prop to your component.
let filters = { 
  'A' : item => item > 4, 
  'B' : item => item > 4 && item < 8,
  'C' : item => item >8 && item < 15
}
let defaultFilter = item => true 

// pick the right filter
let filter = filters[id] || defaultFilter

// now filter the collection with that filter
ar = ar.filter(filter)

With this, you've turned an imperative process of picking the right filter into a declarative process. The filters object now lists the filters without any logic. This is much more flexible and allows for more future refactoring in other parts of the codebase.

One other tip that might help would be rearrange the argument so that item is in the middle, and the numbers go from smallest to largest:

item => 4 < item && item < 8

(See this answer for more)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM