DEV Community

Cover image for A 12-Line JavaScript Function to Get All Combinations of an Object's Properties
Nick Scialli (he/him)
Nick Scialli (he/him)

Posted on

A 12-Line JavaScript Function to Get All Combinations of an Object's Properties

It's often desirable to get all combinations of an object's properties. One of the cases where I use this most is testing: I want to try a function of component with every permutation of data it might receive.

A Simple Example: React Navbar

Let's say we have a React navbar that takes the following props: name, displayMode, and timezone. name can either be a string or null (if there's no logged-in user), displayMode can be "dark" or "light", and timezone can be "ET", "CT", "MT", or "PT".

We would like to do snapshot testing of our navbar in each possible state. We could manually write out each state, but that ends up being 2 * 2 * 4 = 16 combinations. If we had even more props, this would get out of hand fast! Instead, let's write a quick script that produces all possible combinations:

const navBarProps = {
  name: ["John Doe", null],
  displayMode: ["dark", "light"],
  timezone: ["ET", "CT", "MT", "PT"],
};

function allCombinations(obj) {
  let combos = [{}];
  Object.entries(obj).forEach(([key, values]) => {
    let all = [];
    values.forEach((value) => {
      combos.forEach((combo) => {
        all.push({ ...combo, [key]: value });
      });
    });
    combos = all;
  });
  return combos;
}

console.log(allCombinations(navBarProps));

/*

[ { name: 'John Doe', displayMode: 'dark', timezone: 'ET' },
  { name: null, displayMode: 'dark', timezone: 'ET' },
  { name: 'John Doe', displayMode: 'light', timezone: 'ET' },
  { name: null, displayMode: 'light', timezone: 'ET' },
  { name: 'John Doe', displayMode: 'dark', timezone: 'CT' },
  { name: null, displayMode: 'dark', timezone: 'CT' },
  { name: 'John Doe', displayMode: 'light', timezone: 'CT' },
  { name: null, displayMode: 'light', timezone: 'CT' },
  { name: 'John Doe', displayMode: 'dark', timezone: 'MT' },
  { name: null, displayMode: 'dark', timezone: 'MT' },
  { name: 'John Doe', displayMode: 'light', timezone: 'MT' },
  { name: null, displayMode: 'light', timezone: 'MT' },
  { name: 'John Doe', displayMode: 'dark', timezone: 'PT' },
  { name: null, displayMode: 'dark', timezone: 'PT' },
  { name: 'John Doe', displayMode: 'light', timezone: 'PT' },
  { name: null, displayMode: 'light', timezone: 'PT' } 

*/
Enter fullscreen mode Exit fullscreen mode

And that's it! Now, if we wanted to generate some components based on these combinations, we can just loop over the array we created.

const tests = allCombinations(navBarProps).map(props => {
  return <Navbar {...props} />;
});
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
dtinth profile image
Thai Pangsakulyanont

At first I assumed it doing this requires a recursive-like algorithm, never thought of doing it that way!

Using flatMap, we can further reduce this down to 8 lines:

function allCombinations(obj) {
  let combos = [{}]
  for (const [key, values] of Object.entries(obj)) {
    combos = combos.flatMap((combo) =>
      values.map((value) => ({ ...combo, [key]: value }))
    )
  }
  return combos
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nas5w profile image
Nick Scialli (he/him)

ooo that's fancy!

Collapse
 
romainblatrix profile image
Romain Blatrix

or even shorter :

type PropObject = Record<string, unknown[]>;

export const getCombinations = (obj: PropObject): PropObject[] =>
  Object.entries(obj).reduce(
    (acc, [key, values]) =>
      acc.flatMap((combo) =>
        values.map((value) => ({ ...combo, [key]: value }))
      ),
    [{}]
  );
Enter fullscreen mode Exit fullscreen mode

thanks for the inspiration :)