Object and Array Spread/Destructuring/Rest Usage

2019-02-22

Foreword

With the rise of ES2015 and beyond there were quite a few features introduced. The ones which I find myself using a lot are all the slice and dice features:

Here I’m not trying to explain what they do, but rather show where I found them useful in one way or the other. So, after some thinking, I’ve compiled a list.

Note most of the uses here create new throwaway objects. This might trigger garbage collection and have some performance hits. But, if used sparingly, I found these nicer on the eye once I got used to them. Your mileage might vary.

Omitting Items

One of the handy functions in lodash is omit which allows you to return an object without specified keys. With arrow functions and destructuring this could be done like:

  array.map(({ omit1, omit2, omit3, ...rest }) => (rest));

Here omit 1,2 and 3 are the keys which we don’t need.

Picking Items

While sometimes we want to omit a few keys, other times we need to pick just a few from the big objects. To do so we can alter the previous function:

  array.map(({
    pick1, pick2, pick3, ...rest
  }) => ({
    pick1, pick2, pick3
  }));

this would provide a new object with the pick 1, 2 and 3 keys.

Transforming Object

By combining previous approaches it’s possible to transform objects, either by nesting or flattening the properties:

  arr.map(({
    obj1, obj2
  }) => ({
    ...obj1, ...obj2,
  }));

Conditional Adding of Items

Sometimes you want to add some properties based on a condition. This can be achieved by combining object spread and the fact that empty object doesn’t insert any new values:

  const obj = {
  name: 'miau',
    ...(condition ? { says: 'kuku' } : {})
  };

Using in Reducers

Reducer is the main building block for any functional style operation. You can build filter and map and all the other from it. Because it’s versatile, there are few use-cases for spreading there.

Main Idea

You would use reducers with the concept of the accumulator. Usually, you want to use the previous accumulator value + some new data. So, in general, the idea is to spread the accumulator first and then do some computation, this looks something like:

  arr.reduce((acc, obj) => ({
    ...acc,
    // add some new properties here
  }),{})

Getting all the keys from an array of mixed objects

Every now and then I need to generate CSV, rather than reaching out for the library, it’s possible to generate it by hand. One of the tasks there is getting all the titles for the columns. With the usage of object spread it can be done like so:

  const keyObj = arr.reduce((acc, obj) => ({ ...acc, ...obj }), {});
  const keys = Object.keys(keyObj);

Here we don’t really care about the values, so they can overwrite each other on every step.

Keying by property

In lodash, there is a function called keyBy which takes a key name and creates an object with keys based on the value of passed key name. This can also be done with reduce and object spread.

  arr.reduce((acc, { key, ...rest }) => ({
        ...acc,
        [key]: {key, ...rest}
        }), {});

Note that this will have the last occurrence of the same id.

Grouping by property

Similar to the example above it’s possible to group objects with the same key.

  arr.reduce((acc, { key, ...rest }) => ({
        ...acc,
        [key]: [{ key, ...rest }, ...(acc[key] ? acc[key] : []],
      }), {});

React Usage

Since react has to be transpiled it’s really easy to get started with the modern features. Some of the patterns I’ve found using rest spread in react.

React same named props

Sometimes you have a component which wraps several children, slicing and passing down the props to children. This works quite well with destructuring and spread if props have the same names:

  const Child1 = ({ child1Prop1, child1Prop2 }) => (
  // return jsx
  );

  const Child2 = ({ child2Prop1, child2Prop2 }) => (
  // return jsx
  );

  const Parent = ({ child1Prop1, child1Prop2, child2Prop1, child2Prop2 }) => (
    <>
      <Child1 {...{ child1Prop1, child1Prop2 }} />
      <Child2 {...{ child2Prop1, child2Prop2 }} />
    </>
  );

Overriding default styles

While inline styles are not generally the best idea in the world, sometimes they are the quickest way to get something out. One way to make them extendable is to allow overwriting with object spread.

  const Comp = ({ styles }) => (
    <div styles={{ ...defaultStyles, ...styles }}>
     {/* your stuff here */}
    </div>
  );

Array Methods on Iterables

Sometimes you might get NodeList or something similar while working with the DOM. In order to use the filter, map and so on you need to convert iterable to an array. To do so you can simply spread the NodeList, what will give you the array you need.

  [...document.querySelectorAll('p')]
    .map(/* do your stuff*/)

Conclusion

Some of these will make your code easier to reason about because of immutability. But as mentioned in the beginning some of these might have an impact on performance.

In the end, you choose what’s appropriate for the use-case. I just prefer the ergonomics. And hopefully, some of these patterns will be optimized in the runtime/compiler.