如何在 JavaScript 中创建此对象的 keys/values 的所有组合?

How can I create all combinations of this object's keys/values in JavaScript?

我有以下 JavaScript 对象结构:

var options = {
    optionOne: [true, false],
    optionTwo: [true, false],
    optionThree: [
        null,
        {property1: 9, property2: 7},
        {property1: 4, property2: 12},
        {property1: 16, property2: 14}
    ]
};

请注意,此对象中 key/pairs 的数量会有所不同。所以实际上可能有 optionFouroptionFive 等,每个选项的数组可以有任意数量或类型的值。

我需要遍历此对象并创建一个包含所有可能选项组合的对象的数组:

[
    {optionOne: true,  optionTwo, true,  optionThree: null},
    {optionOne: false, optionTwo, true,  optionThree: null},
    {optionOne: true,  optionTwo, false, optionThree: null},
    {optionOne: false, optionTwo, false, optionThree: null},
    {optionOne: true,  optionTwo, true,  optionThree: {property1: 9, property2: 7}},
    {optionOne: false, optionTwo, true,  optionThree: {property1: 9, property2: 7}},
    {optionOne: true,  optionTwo, false, optionThree: {property1: 9, property2: 7}},
    {optionOne: false, optionTwo, false, optionThree: {property1: 9, property2: 7}},
    {optionOne: true,  optionTwo, true,  optionThree: {property1: 4, property2: 12}},
    {optionOne: false, optionTwo, true,  optionThree: {property1: 4, property2: 12}},
    {optionOne: true,  optionTwo, false, optionThree: {property1: 4, property2: 12}},
    {optionOne: false, optionTwo, false, optionThree: {property1: 4, property2: 12}},
    {optionOne: true,  optionTwo, true,  optionThree: {property1: 16, property2: 14}},
    {optionOne: false, optionTwo, true,  optionThree: {property1: 16, property2: 14}},
    {optionOne: true,  optionTwo, false, optionThree: {property1: 16, property2: 14}},
    {optionOne: false, optionTwo, false, optionThree: {property1: 16, property2: 14}}
]

我正在为如何实现这一点而苦苦挣扎,但我相当有信心答案在于递归。

算法大神能帮帮我吗?

function getCombinations(options, optionIndex, results, current) {
    var allKeys = Object.keys(options);
    var optionKey = allKeys[optionIndex];

    var vals = options[optionKey];

    for (var i = 0; i < vals.length; i++) {
        current[optionKey] = vals[i];

        if (optionIndex + 1 < allKeys.length) {
            getCombinations(options, optionIndex + 1, results, current);
        } else {
            // The easiest way to clone an object.
            var res = JSON.parse(JSON.stringify(current));
            results.push(res);
        }
    }

    return results;
}

这样使用:

var results = getCombinations(options, 0, [], {});

这是一个有效的 JSFiddle example

这是一个改进,基于 Dmytro 的 :

function getPermutations(object, index = 0, current = {}, results = []) {
  const keys = Object.keys(object);
  const key = keys[index];
  const values = object[key];

  for (const value of values) {
    current[key] = value;
    const nextIndex = index + 1;

    if (nextIndex < keys.length) {
      this.getPermutations(object, nextIndex, current, results);
    } else {
      const result = Object.assign({}, current);
      results.push(result);
    }
  }
  return results;
}

改进:

  • 适用于任何类型的值,即使该值是函数
  • 默认参数值,可以通过以下方式轻松调用:const p = getPermutations(object);
  • 细微的语义改进

最近复活了,我认为现代 JS 提供了一种更简洁的编写方式。

const crossproduct = (xss) => 
  xss.reduce((xs, ys) => xs.flatMap(x => ys.map(y => [...x, y])), [[]])

const combinations = (o, keys = Object .keys (o), vals = Object .values (o)) =>
  crossproduct(vals).map(xs => Object.fromEntries(xs.map ((x, i) => [keys[i], x])))


const options = {optionOne: [true, false], optionTwo: [true, false], optionThree: [null, {property1: 9, property2: 7}, {property1: 4, property2: 12}, {property1: 16, property2: 14}]}

console .log (JSON .stringify (
  combinations (options)
, null, 4))
.as-console-wrapper {max-height: 100% !important; top: 0}

我们从一个 crossproduct 函数开始,例如,需要

[[1, 2], ['a', 'b', 'c'], ['T', 'F']]

和returns

[
  [1, 'a', 'T'], [1, 'a', 'F'], [1, 'b', 'T'], [1, 'b', 'F'], [1, 'c', 'T'], [1, 'c', 'F'], 
  [2, 'a', 'T'], [2, 'a', 'F'], [2, 'b', 'T'], [2, 'b', 'F'], [2, 'c', 'T'], [2, 'c', 'F']
]

然后 combinations 将我们的对象与 Object.keys 分开,Object.values 将值传递给 crossproduct,然后对于结果中的每个数组,映射到值,将相应的键与每个值相关联,然后用 Object.fromEntries.

重新水合一个对象

在我看来,这个顺序是结果的逻辑顺序。但是,如果我们将 crossproduct 的返回表达式替换为以下内容,我们将得到问题中提到的顺序:

  xss .reduce ((xs, ys) => ys .flatMap (y => xs .map (x => [...x, y])), [[]])