0

I am changing a object twice from and array of objects such that in the first iteration I filter out a few objects and in the second I change each of the filtered objects using map. Can I use reducer or something better?

  const originalTree = html.parse(text);

  if (originalTree[0].type === 'text') {
    return text;
  }
  const result = originalTree
    .map((obj) => ({
      ...obj,
      children: obj.children.filter((el) => el.name !== 'style'),
    }))
    .map((obj) => ({
      ...obj,
      children: obj.children.map(function(child) {
        child.attrs = {};
        return child;
      }),
    }));

  return html.stringify(result);
}
Yevgen Gorbunkov
  • 12,646
  • 3
  • 13
  • 31
Ackman
  • 1,395
  • 4
  • 23
  • 39
  • You can always [use transducers](https://stackoverflow.com/questions/44198833/how-to-chain-map-and-filter-functions-in-the-correct-order/) – VLAZ Jan 21 '20 at 15:51
  • I don't think a reducer makes sense here, as a reducer accumulates everything to a single final value. It looks like you want to keep your array intact. – Cal Irvine Jan 21 '20 at 15:51
  • @CalIrvine you can reduce to a single...array. A mapping operation can be defined as `arr.reduce((result, item, index, array) => [...result, mappingFn(item, index, array)], [])` – VLAZ Jan 21 '20 at 15:53
  • Can you give a sample of your `originalTree` at each step, including desired output? – Yevgen Gorbunkov Jan 21 '20 at 15:53
  • 1
    Wouldn't just chaining on the `obj.children` makes more sense than mapping twice on `originalTree`? – Emile Bergeron Jan 21 '20 at 15:53
  • Yes this is the original question https://stackoverflow.com/questions/59686302/removing-elements-from-an-array-within-an-array/59686410?noredirect=1#comment105532907_59686410 – Ackman Jan 21 '20 at 15:55
  • Just do `children: obj.children.filter(/* */).map(/* */)`. – Emile Bergeron Jan 21 '20 at 15:57
  • @VLAZ True, but that seems more convoluted than just using a map, in this case. – Cal Irvine Jan 21 '20 at 15:57
  • Since this is tagged as React, is the data immutable here? – Emile Bergeron Jan 21 '20 at 16:02
  • 1
    @CalIrvine it was an example. You could do a lot more - you could have logic to map *and* filter at the same time, for example. Or do other types of manipulations - fill in specific array indeces and producing sparse arrays instead of only dense ones like `map` would do. `reduce` simply gives you more control, how you use that is up to you. And indeed transducers take the concept of array iterators to another level - you get a *chainable* and *lazy* iteration that is efficient as it goes over the entire array once, even if you do 7 mappings and 5 filters. Transducers are build on `reduce`. – VLAZ Jan 21 '20 at 16:04

1 Answers1

4

Here's a way to loop with one originalTree.map(), also simplifying your children.filter().map() chain to a children.flatMap() call:

const result = originalTree.map(
  obj => ({
    ...obj,
    children: obj.children.flatMap(
      el => el.name !== 'style'
        ? [{ ...el, attrs: {} }]
        : []
    )
  })
);

If you would rather continue using filter() and map() separately on obj.children, I would suggest at least adding attrs immutably instead of modifying the existing references:

const result = originalTree.map(
  obj => ({
    ...obj,
    children: obj.children.filter(
      el => el.name !== 'style'
    ).map(
      child => ({ ...child, attrs: {} })
    )
  })
);
Patrick Roberts
  • 40,065
  • 5
  • 74
  • 116