Amin NAIRI
Amin NAIRI

Reputation: 2504

Generate all combinations of a multidimensional object

Problem

I would like to generate all combinations for a template-like object which could take several children in which some define the desired output as an array. Those children can have as well children (there is virtually no limit).

Example Input

const input = {
  a: [true, false],
  b: ['first', 'second'],
  c: {
    d: [true, false]
  }
};

Example Output

const output = [
  {
    a: true,
    b: 'first',
    c: {
      d: true
    }
  },
  {
    a: true,
    b: 'first',
    c: {
      d: false
    }
  },
  {
    a: true,
    b: 'second',
    c: {
      d: true
    }
  },
  {
    a: true,
    b: 'second',
    c: {
      d: false
    }
  },
  //...
]

Question

What could be the JavaScript function to transform the input into an output?

What has been tried?

I tried taking the original function from this question and make it more ES compliant but this is not working when having multiple children inside the object.

function combinations(input, keyIndex = 0, current = {}, result = []) {
  const keys = Object.keys(input)
  const key = keys[keyIndex]
  const values = input[key]

  for (const index in values) {
    current[key] = values[index]

    if (keyIndex + 1 < keys.length) {
      combinations(input, keyIndex + 1, current, result)
    } else {
      result.push(JSON.parse(JSON.stringify(current)))
    }
  }

  return result;
}

Upvotes: 2

Views: 124

Answers (1)

Nina Scholz
Nina Scholz

Reputation: 386560

You could take a recursive function which separates all key/value pairs and build a new cartesian product by iterating the values, if an array with objects call getCartesian again and build new objects.

This works for nested objects as well.

function getCartesian(object) {
    return Object.entries(object).reduce((r, [k, v]) => {
        var temp = [];
        r.forEach(s =>
            (Array.isArray(v) ? v : [v]).forEach(w =>
                (w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
                    temp.push(Object.assign({}, s, { [k]: x }))
                )
            )
        );
        return temp;
    }, [{}]);
}

var input = { a: [true, false], b: ['first', 'second'], c: { d: [true, false] } },
    cartesian = getCartesian(input);

console.log(cartesian);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 4

Related Questions