Javascript 同时可能的唯一元素组合的输出数组

Javascript 同时可能的唯一元素组合的输出数组,javascript,arrays,recursion,set,Javascript,Arrays,Recursion,Set,我的应用程序引用了充当目录的数据库对象。它是一个项目目录,如果用户有必要的组件,就可以精心编制。以下是目录的一个小示例: const itemCatalog = { "bramble_vest" : { "components" : [ "Chain Vest", "Chain Vest" ], "name" : "Bramble Vest&quo

我的应用程序引用了充当目录的数据库对象。它是一个项目目录,如果用户有必要的组件,就可以精心编制。以下是目录的一个小示例:

const itemCatalog = {
    "bramble_vest" : {
        "components" : [ "Chain Vest", "Chain Vest" ],
        "name" : "Bramble Vest"
    },
    "guardian_angel" : {
        "components" : [ "B.F. Sword", "Chain Vest" ],
        "name" : "Guardian Angel"
    },
    "hextech_gunblade" : {
        "components" : [ "B.F. Sword", "Needlessly Large Rod" ],
        "name" : "Hextech Gunblade"
    },
    "locket_of_the_iron_solari" : {
        "components" : [ "Chain Vest", "Needlessly Large Rod" ],
        "name" : "Locket of the Iron Solari"
    },
    "morellonomicon" : {
        "components" : [ "Giant's Belt", "Needlessly Large Rod" ],
        "name" : "Morellonomicon"
    },
    "sunfire_cape" : {
        "components" : [ "Chain Vest", "Giant's Belt" ],
        "name" : "Sunfire Cape"
    },
    "zekes_herald" : {
        "components" : [ "B.F. Sword", "Giant's Belt" ],
        "name" : "Zeke's Herald"
    }
}
当用户拥有任何给定项目的必要组件时,用户可以组装该项目。用户被任意和随机授予组件,但用户如何接收组件与我的问题无关。只需说,用户的组件被放入客户机上的一个数组中,然后该数组用于确定用户可以组装哪些项:

let myComponents = [
    "B.F. Sword",
    "Chain Vest",
    "Giant's Belt",
    "Chain Vest",
    "Needlessly Large Rod"
]
我已经编写了一段代码,确定哪些项目可以使用
myComponents
中的元素。这是相当直接的,即使它不是特别简洁或时尚

使用
myComponents
中列出的组件,此
itemCatalog
示例中的所有项目都是可能的。然而,它们不是同时发生的。当然,这是因为没有足够的组件用于所有项目

我需要能够同时确定哪些项目是可能的的逻辑,因为当参照
项目目录
时,
myComponents
中的组件。输出应该是数组的数组。每个内部数组都是同时可能的目录项的列表。在这种情况下,如果组件当前位于
myComponents
中,则如下所示:

[ 
    ["Bramble Vest", "Hextech Gunblade"], 
    ["Bramble Vest", "Morellonomicon"], 
    ["Bramble Vest", "Zeke's Herald"], 
    ["Guardian Angel", "Locket of the Iron Solari"], 
    ["Guardian Angel", "Morellonomicon"], 
    ["Guardian Angel", "Sunfire Cape"], 
    ["Hextech Gunblade", "Sunfire Cape"], 
    ["Locket of the Iron Solari", "Sunfire Cape"], 
    ["Locket of the Iron Solari","Zeke's Herald"]
]
下面是我目前的逻辑。有很多日志记录可以帮助筛选,但是函数
buildSimultaneousItems()
的主要问题是,一旦在迭代过程中根据另一个项目检查了一个项目,就不会再次检查这两个项目。我不想涉入太多,因为我不想用信息过载吓跑人们。这一切都很简单,尽管很丑陋。最主要的是,预期产出高于预期。请随时提问

//可以使用组件组装的项目目录。
//应用程序将此作为参考。此目录在应用程序中更大,包含更多项目。
常量项目录={
“荆棘背心”:{
“组件”:[“链背心”、“链背心”],
“名称”:“荆棘背心”
},
“守护天使”:{
“组件”:[“B.F.剑”、“链背心”],
“姓名”:“守护天使”
},
“hextech_枪刃”:{
“组件”:[“B.F.剑”,“不必要的大杆”],
“名称”:“Hextech枪刃”
},
“铁制索拉里吊坠”:{
“组件”:[“链背心”,“不必要的大杆”],
“名称”:“索拉里铁制小盒”
},
“morellonomicon”:{
“组件”:[“巨人带”,“不必要的大杆”],
“名称”:“Morellonomicon”
},
“太阳火角”:{
“组件”:[“链背心”、“巨人腰带”],
“名称”:“太阳火角”
},
“泽克斯先驱报”:{
“组件”:[“B.F.剑”、“巨人腰带”],
“姓名”:“齐克的先驱”
}
}
//用户当前拥有的组件
设myComponents=[
“B.F.剑”,
“链背心”,
“巨人腰带”,
“链背心”,
“不必要的大棒”
]
//返回具有提供的组件组合(myComponents)的可能项的数组
getPossibleItems=(arr)=>{
设possibleItems=[];
用于(arr中的常数项){
if(doArraysMatch(arr[possItem].components,myComponents)==true){
possibleItems.push(arr[possibleItem].name);
}
}
返回可能项;
}
//返回与上述函数中返回的数组相对应的索引处的组件数组
getPossItemsComponents=(arrA,arrB)=>{
让possItemsComponents=[]
用于(arrA中的常量项){
用于(arrB中的const COMB项目){
日志(arrB[combItem].name,“:”,arrB[combItem].components);
if(arrA[item]==arrB[combItem].name){
possItemsComponents.push(arrB[combItem].components);
}
}
}
返回possiitemscomponents;
}
//尝试返回数组的数组。每个内部数组都是一个可以
//与提供的组件(myComponents)同时组装
buildSimultaneousItems=()=>{
设项=[];
possibleItems=getPossibleItems(itemCatalog);
possibleItemsComponents=GetPossibleItemsComponents(possibleItems,itemCatalog);
for(设i=0;i{
const subsetCount=u.countBy(子集);
const supersetCount=\ countBy(超集);
返回u.every(subsetCount,(count,value)=>supersetCount[value]>=count);
}
containsAllItems=(arrA,arrB)=>{
// are required components (req) a subset of inventory (inv) ?
const canMakeItem = (req, inv) => req.every(r => inv.indexOf(r) > -1);
// choose k items from array
const chooseCombos = (arr, k, prefix=[]) => {
  if (k == 0) return [prefix];
  return arr.flatMap((v, i) =>
    chooseCombos(arr.slice(i+1), k-1, [...prefix, v])
  );
}
// array difference with dupes
const remainingComponents = (a, b) => {
  return a.filter(function(v) {
    return !this.get(v) || !this.set(v, this.get(v) - 1);
  }, b.reduce( (acc, v) => acc.set(v, (acc.get(v) || 0) + 1), new Map() ));
}
// eliminate combos (arrs[n]) with insufficient inventory (inv) for all items in combo
const createableCombos = (arrs, inv) => { 
  return arrs.filter(arr => {
    // we know arr[0] is possible so remove components
    let currentComponents = remainingComponents(myComponents, itemCatalog[arr[0]].components);
    // check subsequent array items
    for (let i=1; i<arr.length; i++) {
      let requiredComponents = itemCatalog[arr[i]].components;
      if (!canMakeItem(requiredComponents, currentComponents)) {
        // this combo cannot be made from available components
        return false
      } else {
        // remove components from inventory for this item
        currentComponents = remainingComponents(currentComponents, requiredComponents);
      }
    }
    // we can make all the items in this combo!
    return true;
  });
}
[
  [violin, guitar],
  [guitar, trumpet],
  [violin, trumpet]
]
// eliminate single items not able to be made
// in the example all items can be created
let createableSingleItems = Object.keys(itemCatalog)
    .filter(k => canMakeItem(itemCatalog[k].components, myComponents) );

// candidate pairs - pairs is n choose _2_
let candidatePairs = chooseCombos(createableSingleItems, 2, []);
let createablePairs = createableCombos (candidatePairs, myComponents)

// candidate triples - n choose _3_
let candidateTriples = chooseCombos(createableSingleItems, 3, []);
let createableTriples = createableCombos (candidateTriples, myComponents);
// candidate pairs - pairs is n choose _2_
let candidatePairs = chooseCombos(createableSingleItems, 2, []);
let createablePairs = createableCombos (candidatePairs, myComponents)
[
    ["bramble_vest", "hextech_gunblade"],
    ["bramble_vest", "morellonomicon"],
    ["bramble_vest", "zekes_herald"],
    ["guardian_angel", "locket_of_the_iron_solari"],
    ["guardian_angel", "morellonomicon"],
    ["guardian_angel", "sunfire_cape"],
    ["hextech_gunblade", "sunfire_cape"],
    ["locket_of_the_iron_solari", "sunfire_cape"],
    ["locket_of_the_iron_solari", "zekes_herald"]
]
createablePairs.map(a => a.map(b => itemCatalog[b].name));
[
    ["Bramble Vest", "Hextech Gunblade"],
    ["Bramble Vest", "Morellonomicon"],
    ["Bramble Vest", "Zeke's Herald"],
    ["Guardian Angel", "Locket of the Iron Solari"],
    ["Guardian Angel", "Morellonomicon"],
    ["Guardian Angel", "Sunfire Cape"],
    ["Hextech Gunblade", "Sunfire Cape"],
    ["Locket of the Iron Solari", "Sunfire Cape"],
    ["Locket of the Iron Solari", "Zeke's Herald"]
]