0

Consider I have an array-object which looks like this

const options = [{ label: "" }, { label: "123" }]

Now, I want to filter it and remove object which have label value equal to "" and then add order property equivalent to index to those object in an array having some value for label

I am able to do with combination of lodash filter and map but I was thinking if I could do it in a better way (probably using just filter?)

    const options = _.filter(options, "label").map(
      (option, position) => {
        return {
          ...option,
          order: position
        }
      }
    )
Alwaysblue
  • 9,948
  • 38
  • 121
  • 210

3 Answers3

2

I personally think that what you have now is fine as its quite readable. One different idea could be to use _.flatMap() instead, which will only require one iteration of your options array. If the label is present in an object then you can return the modified object. If the label isn't present then you can return an empty array. As you're flattening into the resulting array for every value you return, the empty array won't be included in the resulting array.

const options = [{ label: "" }, { label: "123" }];
const res = _.flatMap(options, (o, order) => o.label ? {...o, order} : []);
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

Note: You can also use the vanilla version of .flatMap(), so you don't need to use lodash unless you need good browser support or are using it for other code.

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
0

as i understand , you can use the pure js filter :

const options = [{
  label: ""
}, {
  label: "123"
}]
var index = 0;
const res = options.filter((e, i) => {
  e = Object.assign(e,{});
  if (e.label) {
    e.order = index++;
    return e;
  }
})
console.log(res)

Edit: you can also use map and new array to call one function and remain your source pure:

const options = [{
  label: ""
}, {
  label: "123"
}]


var newArr = [];
options.map((e,r)=>{
    if(e.label){
        newArr.push({...e,order:newArr.length});
    }
})

console.log(options)
console.log(newArr)
PoryaGrand
  • 91
  • 6
  • This mutates the original objects, which makes your `filter()` method impure when it should really be pure. – Nick Parsons Jul 27 '20 at 03:00
  • @NickParsons no problem. just clone `e` inside itself. like `e = Object.assign(e,{});` before the `if`; or other ways you want. – PoryaGrand Jul 27 '20 at 04:06
  • Doing `Object.assing(e, {})` doesn't clone `e`, it just merges an empty object with `e`, so your filter method is still impure. To clone you want to use it the other way: `Object.assing({}, e)`, but if you did this then your code won't work. – Nick Parsons Jul 27 '20 at 04:11
  • you are right.it is not work in filter. so you can use `map` instead of `filter`. – PoryaGrand Jul 27 '20 at 04:38
  • The issue with using `.map()` like that is that you're only using it for its loop functionality. The `.map()` method is supposed to transform one element into another element. So if you provide it with an arary of `N` elements, the `.map()` method will return an array of `N` elements. Since you're not using the return value of the `.map()` method, you should consider using `.forEach()` instead, which is better for callbacks which cause side-effects like adding to an external array. – Nick Parsons Jul 27 '20 at 04:42
  • of course `filter` will just do this, if you want to loop inside an array you must visit all its elements, both of them call a function from js and pass each element to that function so they are somehow the same. please let me know what exactly want to do that the performance is so important to you like this? – PoryaGrand Jul 27 '20 at 04:54
  • I'm not talking about performance, I'm mainly talking about what is semantically correct. For example, you **wouldn't** use `.filter((e) => console.log(e))` to print out the elements in a loop, because `.filter()` isn't a method which was made for the sole purpose of looping over an array. Same idea goes for `.map()`, you shouldn't be using it for looping over an array _only_, as that's not what it's intended for, the method which you're suppose to use for that is `.forEach()` ;) – Nick Parsons Jul 27 '20 at 05:02
  • Please see: [Is performing a mapping operation without using returned value an antipattern?](https://stackoverflow.com/a/56904505) for further info about this. – Nick Parsons Jul 27 '20 at 05:08
  • yes. to code in pattern , you are correct. but basically they don't difference too much, as you said , you can use `forEach` instead. always you can use every thing. it depends on your needs. there are several correct and incorrect ways . i hope you find the best way. – PoryaGrand Jul 27 '20 at 05:16
0

Another pure JS solution could be:

const options = [{ label: "" }, { label: "123" }];
const modified = options.filter((el) => el.label.length).map((option, i) => ({ ...option, order: i }));
console.log(modified);
Devchris
  • 394
  • 3
  • 12