Better way to write spread operator with conditions in javascript

Viewed 1345

I'm looking for a better way to write the following code:

let fromCreatedAt;
let toCreatedAt = new Date();

const someObject = {
    ...((!!fromCreatedAt || !!toCreatedAt) ? {
        createdAt: {
            ...(!!fromCreatedAt ? {
                from: fromCreatedAt,
            } : {}),
            ...(!!toCreatedAt ? {
                to: toCreatedAt,
            } : {}),
        },
    } : {}),
}

console.log(someObject); // { createdAt: { to: 2020-11-18T05:32:57.697Z } }

fromCreatedAt and toCreatedAt are variables that can change and generate a different object.

This is just an example, but you could have an object that repeats the conditions of the createdAt field multiple times for other fields, so you would find a way to refactor that repeated functionality.

3 Answers

I'd use shorthand properties to create the createdAt nested object, removing undefined values by filtering through Object.fromEntries. Then you can create the someObject depending on whether the createdAt object has any keys or not:

let from;
let to = new Date();
const createdAt = Object.fromEntries(
  Object.entries({ from, to }).filter(([, val]) => val !== undefined)
);

const someObject = Object.keys(createdAt).length
  ? { createdAt }
  : {};
  
console.log(someObject);

You could create objects using shorthand property name and conditionally spread it

let fromCreatedAt,
    toCreatedAt = new Date(),
    from = fromCreatedAt,
    to = toCreatedAt;

const createdAt = {
      ...(from && { from }),
      ...(to && { to })
    },
    someObject = {
      ...(from || to && { createdAt })
    }

console.log(someObject)

Because ...null and ...undefined will cause no data properties to be copied, you could get rid of the ternary operators and simplify to boolean short-circuit evaluation:

let fromCreatedAt;
let toCreatedAt = new Date();

const someObject = {
  ...(fromCreatedAt || toCreatedAt) && {
    createdAt: {
      ...fromCreatedAt && { from: fromCreatedAt},
      ...toCreatedAt && { to: toCreatedAt},
    }
  }
};

console.log(someObject);

I thought this might be a little more elegant, but it doesn't get rid of the duplicate conditions, so probably doesn't answer the question completely.

Related