如何在 JavaScript 中创建该对象的键/值的所有组合?

问题描述 投票:0回答:4

我有以下 JavaScript 对象结构:

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

请注意,该对象中的密钥/对的数量会有所不同。因此,实际上可能有

optionFour
optionFive
等,并且每个选项的数组可以有任意数量或类型的值。

我需要迭代这个对象并创建一个包含所有可能选项组合的对象的数组:

[
    {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}}
]

我正在努力解决如何实现这一目标,但我相当有信心答案在于递归。

算法大神能帮帮我吗?

javascript algorithm recursion combinations permutation
4个回答
15
投票
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 示例


2
投票

这最近又复活了,我认为现代 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']]

并返回

[
  [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])), [[]])

1
投票

这是基于 Dmytro 的answer的改进:

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);
  • 细微的语义改进

0
投票

对于任何寻求基于 TypeScript 答案的人:

export function getCartesian<TOut extends Record<string, unknown[]>>(inputObject: TOut): Array<{ [K in keyof TOut]: TOut[K][number] }> {
// Reduce the entries of the input object to generate the Cartesian product
return Object.entries(inputObject).reduce((accumulator, [key, valuesArray]) => {
    const combinations: Array<{ [K in keyof TOut]: TOut[K][number] }> = [];
    
    accumulator.forEach(existingCombination => {
        valuesArray.forEach(singleValue => {
            // If the value is an object, recursively get its Cartesian product
            const nestedValues = (singleValue && typeof singleValue === 'object' && !Array.isArray(singleValue)) 
                ? getCartesian(singleValue as Record<string, unknown[]>) 
                : [singleValue];
            
            // Combine the existing combination with each nested value
            nestedValues.forEach(nestedValue => {
                combinations.push({ ...existingCombination, [key]: nestedValue });
            });
        });
    });
    
    return combinations;
}, [{} as { [K in keyof TOut]: TOut[K][number] }]);

}

© www.soinside.com 2019 - 2024. All rights reserved.