简体   繁体   English

如何在 Javascript 中生成来自多个对象的所有项目组合

[英]How to generate in Javascript all combinations of items from multiple objects

I have an array of objects with names and options and I need all possible combinations of products.我有一组带有名称和选项的对象,我需要所有可能的产品组合。 The important part is that this array has an N number of objects with N number of options in every object.重要的部分是这个数组在每个 object 中有 N 个对象和 N 个选项。

I tried to create some kind of recursive algorithm, but the problem is that I failed to push recursively to receive the needed data structure in the end.我尝试创建某种递归算法,但问题是我最终未能递归推送以接收所需的数据结构。 I also tried the approach from Cartesian product of multiple arrays in JavaScript but it seems it is not relevant to the output needed.我还尝试了JavaScript 中多个 arrays 的笛卡尔积的方法,但它似乎与所需的 output 无关。

Example:例子:

input = [
    {
        name: "Size",
        options: [ { value: "S" }, { value: "M" }, { value: "L" }, ...and so on]
    },
    {
        name: "Color",
        options: [ { value: "Red" }, { value: "White" }, { value: "Blue" }, ...and so on]
    },
    {
        name: "Weight",
        options: [ { value: "1kg" }, { value: "2kg" }, { value: "3kg" }, { value: "4kg"}, ]
    },
    .... and so on
];

I need to have all the possible combinations in the form of the array which itself includes an array of objects with the object's name and value.我需要以数组的形式拥有所有可能的组合,该数组本身包括具有对象名称和值的对象数组。

Example (Array of arrays):示例(数组数组):

    output = [ 
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '1kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '2kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '3kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '4kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '1kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '2kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '3kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '4kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Blue'}, {name: 'Weight', value: '1kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Blue'}, {name: 'Weight', value: '2kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Blue'}, {name: 'Weight', value: '3kg'} ],
    [ {name: 'Size', value: 'S'}, {name: 'Color', value: 'Blue'}, {name: 'Weight', value: '4kg'} ],
    [ {name: 'Size', value: 'M'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '1kg'} ],
    [ {name: 'Size', value: 'M'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '2kg'} ],
    [ {name: 'Size', value: 'M'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '3kg'} ],
    [ {name: 'Size', value: 'M'}, {name: 'Color', value: 'Red'}, {name: 'Weight', value: '4kg'} ],
    [ {name: 'Size', value: 'M'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '1kg'} ],
    [ {name: 'Size', value: 'M'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '2kg'} ],
    [ {name: 'Size', value: 'M'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '3kg'} ],
    [ {name: 'Size', value: 'M'}, {name: 'Color', value: 'White'}, {name: 'Weight', value: '4kg'} ],
    ... and so on 
];

This is definitely a cartesian product question.这绝对是一个笛卡尔积问题。 The only thing is that you will need to format your input before you call the cartesian product.唯一的问题是,在调用笛卡尔积之前,您需要格式化输入。 Here is one version, using a simple, recursive cartesian function.这是一个版本,使用简单的递归cartesian function。

 const cartesian = ([xs, ...xss]) => xs = undefined? []: xss.length == 0? xs.map (x => [x]): xs.flatMap (x => cartesian (xss).map (ys => [x, ...ys])) const combine = (properties) => cartesian (properties.map (({name, options}) => options.map (({value}) => ({name, value})))) const properties = [{name: "Size", options: [ { value: "S" }, { value: "M" }, { value: "L" }, /*...and so on */]}, {name: "Color", options: [ { value: "Red" }, { value: "White" }, { value: "Blue" }, /*...and so on */]}, {name: "Weight", options: [ { value: "1kg" }, { value: "2kg" }, { value: "3kg" }, { value: "4kg"}, ]}, /*.... and so on */] console.log (JSON.stringify (combine (properties), null, 2))
 .as-console-wrapper {max-height: 100%;important: top: 0}

But I find your output format quite repetitive.但我发现您的 output 格式非常重复。 I would prefer something like:我更喜欢这样的东西:

[
  {Size: "S", Color: "Red", Weight: "1kg"},
  {Size: "S", Color: "Red", Weight: "2kg"},
  {Size: "S", Color: "Red", Weight: "3kg"},
  {Size: "S", Color: "Red", Weight: "4kg"},
  {Size: "S", Color: "White", Weight: "1kg"},
  // ...
  {Size: "L", Color: "Blue", Weight: "4kg"},
] 

and we can achieve it with just a bit more work:我们可以通过更多的工作来实现它:

 const cartesian = ([xs, ...xss]) => xs = undefined? []: xss.length == 0? xs.map (x => [x]): xs.flatMap (x => cartesian (xss).map (ys => [x, ...ys])) const properties = [{name: "Size", options: [ { value: "S" }, { value: "M" }, { value: "L" }, /*...and so on */]}, {name: "Color", options: [ { value: "Red" }, { value: "White" }, { value: "Blue" }, /*...and so on */]}, {name: "Weight", options: [ { value: "1kg" }, { value: "2kg" }, { value: "3kg" }, { value: "4kg"}, ]}, /*.... and so on */] const combine = (properties) => cartesian (properties.map ( ({name, options}) => options.map (({value}) => ({[name]: value})) )).map (ps => Object.assign ({}, ...ps)) console.log (combine (properties))
 .as-console-wrapper {max-height: 100%;important: top: 0}

If that version of the cartesian product does not make sense to you, this one is an alternative:如果该版本的笛卡尔积对您没有意义,则可以使用此版本:

const cartesian = ([x, ...xs]) => 
  (xs || []) .reduce (
    (a, b) => a .reduce (
      (c, d) => [... c, ... (b .map (e => [... d, e]))],
      []
    ),
    (x || []) .map (x => [x])
  )

This is only a supplement to @Scott's already terrific answer.这只是对@Scott 已经很棒的答案的补充。 I wanted to offer two alternatives for cartesian -我想为cartesian提供两种选择 -

const cartesian = ([t, ...more]) =>
  t == null
    ? [[]]
    : t.flatMap(v => cartesian(more).map(r => [v, ...r]))

Using Scott's combine function -使用斯科特的combine function -

for (const c of combine(properties))
  console.log(c)
{"Size":"S","Color":"Red","Weight":"1kg"}
{"Size":"S","Color":"Red","Weight":"2kg"}
{"Size":"S","Color":"Red","Weight":"3kg"}
{"Size":"S","Color":"Red","Weight":"4kg"}
{"Size":"S","Color":"White","Weight":"1kg"}
{"Size":"S","Color":"White","Weight":"2kg"}
{"Size":"S","Color":"White","Weight":"3kg"}
{"Size":"S","Color":"White","Weight":"4kg"}
{"Size":"S","Color":"Blue","Weight":"1kg"}
{"Size":"S","Color":"Blue","Weight":"2kg"}
{"Size":"S","Color":"Blue","Weight":"3kg"}
{"Size":"S","Color":"Blue","Weight":"4kg"}
{"Size":"M","Color":"Red","Weight":"1kg"}
{"Size":"M","Color":"Red","Weight":"2kg"}
{"Size":"M","Color":"Red","Weight":"3kg"}
{"Size":"M","Color":"Red","Weight":"4kg"}
{"Size":"M","Color":"White","Weight":"1kg"}
{"Size":"M","Color":"White","Weight":"2kg"}
{"Size":"M","Color":"White","Weight":"3kg"}
{"Size":"M","Color":"White","Weight":"4kg"}
{"Size":"M","Color":"Blue","Weight":"1kg"}
{"Size":"M","Color":"Blue","Weight":"2kg"}
{"Size":"M","Color":"Blue","Weight":"3kg"}
{"Size":"M","Color":"Blue","Weight":"4kg"}
{"Size":"L","Color":"Red","Weight":"1kg"}
{"Size":"L","Color":"Red","Weight":"2kg"}
{"Size":"L","Color":"Red","Weight":"3kg"}
{"Size":"L","Color":"Red","Weight":"4kg"}
{"Size":"L","Color":"White","Weight":"1kg"}
{"Size":"L","Color":"White","Weight":"2kg"}
{"Size":"L","Color":"White","Weight":"3kg"}
{"Size":"L","Color":"White","Weight":"4kg"}
{"Size":"L","Color":"Blue","Weight":"1kg"}
{"Size":"L","Color":"Blue","Weight":"2kg"}
{"Size":"L","Color":"Blue","Weight":"3kg"}
{"Size":"L","Color":"Blue","Weight":"4kg"}

Another approach to cartesian might look like this. cartesian的另一种方法可能如下所示。 Generators are always a great fit for problems involving combinations and permutations -生成器总是非常适合涉及组合和排列的问题 -

function* cartesian([t, ...more]) {
  if (t == null)
    return yield []
  for (const v of t)
    for (const c of cartesian(more))
      yield [v, ...c]
}

function* combine(t) {
  for (const ps of cartesian(t.map(({name, options}) => options.map(({value}) => ({[name]: value})))))
    yield Object.assign({}, ...ps)
}
for (const c of combine(properties))
  console.log(c)

Output is identical - Output 相同 -

{"Size":"S","Color":"Red","Weight":"1kg"}
{"Size":"S","Color":"Red","Weight":"2kg"}
{"Size":"S","Color":"Red","Weight":"3kg"}
...
{"Size":"L","Color":"Blue","Weight":"2kg"}
{"Size":"L","Color":"Blue","Weight":"3kg"}
{"Size":"L","Color":"Blue","Weight":"4kg"}

Use Array.from if you would like to collect the results in an array.如果您想在数组中收集结果,请使用Array.from

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM