打字稿:无需重复即可获取固定长度的所有组合

时间:2020-05-03 21:51:08

标签: javascript typescript algorithm ecmascript-6 combinations

我有以下输入内容:

interface Option{
  name:string
  travelMode:string
}

const options:Option[] = [
  {
    name:"john",
    travelMode:"bus"
  },
  {
    name:"john",
    travelMode:"car"
  },
  {
    name:"kevin",
    travelMode:"bus"
  },
  {
    name:"kevin",
    travelMode:"car"
  },
]

我想在此集合中获得长度2的所有可能组合,为此,我正在这样做:

const getCombinations=(options:Option[],startIndex:number,combination:Option[],combinationSize:number)=>{
  if (combination.filter(e => e!==undefined).length === combinationSize)
  {
    console.log(combination)
  }
  else if (startIndex<options.length){
    combination[startIndex]=undefined
    getCombinations(options,startIndex+1,combination,combinationSize)


    combination[startIndex]=options[startIndex]
    getCombinations(options,startIndex+1,combination,combinationSize)
  }
}

getCombinations(options,0,[],2)

这似乎运行良好,我得到以下输出:

[
  undefined,
  undefined,
  { name: 'kevin', travelMode: 'bus' },
  { name: 'kevin', travelMode: 'car' }
]
[
  undefined,
  { name: 'john', travelMode: 'car' },
  undefined,
  { name: 'kevin', travelMode: 'car' }
]
[
  undefined,
  { name: 'john', travelMode: 'car' },
  { name: 'kevin', travelMode: 'bus' },
  undefined
]
[
  { name: 'john', travelMode: 'bus' },
  undefined,
  undefined,
  { name: 'kevin', travelMode: 'car' }
]
[
  { name: 'john', travelMode: 'bus' },
  undefined,
  { name: 'kevin', travelMode: 'bus' },
  undefined
]
[
  { name: 'john', travelMode: 'bus' },
  { name: 'john', travelMode: 'car' },
  undefined,
  undefined
]

但是,我有一个怀疑和一个仍然存在的问题

我的疑问:为什么所有打印的组合的长度都为4,如果我们已经定义了2个元素,我的折断条件通常应该停止递归:我不明白为什么最后一个组合打印在我的输出中包含4个元素(前2个已定义,其余2个未定义)=>即使我的程序已经有2个元素组合,我的程序仍会继续迭代,那不是我想要的

仍然存在的问题:我不想计算名称相同的组合,我只想组合两个不同的名称(john和kevin,但不想john和john或kevin和kevin) 为此,我首先想让程序保持这样,计算所有内容,最后删除带有重复名称的组合,但是我很快意识到这不是最佳解决方案,尤其是当我的输入选项将包含更多数据时(其他人和其他人的选项)。 因此,我尝试了以下解决方案(如果我们已经拜访了个人,则停止该程序):

const getCombinations=(options:Option[],startIndex:number,combination:Option[],combinationSize:number)=>{
  if (combination.filter(e => e!==undefined).length === combinationSize)
  {
    console.log(combination)
  }
  else if (startIndex<options.length){
    combination[startIndex]=undefined
    getCombinations(options,startIndex+1,combination,combinationSize)

    let individualAlreadyVisited = false
    if (startIndex>0)
    {
      for (let i =0;i<startIndex;i++)
      {
        if (combination[i] && combination[i].name===options[startIndex].name)
        {
          individualAlreadyVisited=true
          break
        }
      }
    }

    if (!individualAlreadyVisited)
    {
      combination[startIndex]=options[startIndex]
      getCombinations(options,startIndex+1,combination,combinationSize)
    }
  }
}

getCombinations(options,0,[],2)

但这不能按预期工作,我得到以下输出:

[
  undefined,
  undefined,
  { name: 'kevin', travelMode: 'bus' },
  { name: 'kevin', travelMode: 'car' }
]
[
  undefined,
  { name: 'john', travelMode: 'car' },
  undefined,
  { name: 'kevin', travelMode: 'car' }
]
[
  undefined,
  { name: 'john', travelMode: 'car' },
  { name: 'kevin', travelMode: 'bus' },
  undefined
]
[
  { name: 'john', travelMode: 'bus' },
  undefined,
  { name: 'kevin', travelMode: 'bus' },
  undefined
]
  1. 我仍然可以使用重复的名称,请参阅第一个元素
  2. 我看到一些缺少的组合,例如,未显示以下内容:

     { name: 'john', travelMode: 'bus' },
     { name: 'kevin', travelMode: 'car' }
    

在此先感谢您的帮助,我已经花了几个小时试图理解并得到我想要的东西。

1 个答案:

答案 0 :(得分:0)

我认为混淆来自递归步骤中的数组变异。这使得代码难以理解和调试。

我将使用生成器函数遍历数组,获取当前索引处的元素,并从以下所有具有n-1个元素的元素中获取组合:

 function* combine<T>(array: T[], n: number, start = 0, prev: T[] = []) {
   if(n <= 0) {
     yield prev;
     return;
   }

   for(let i = start; i <= array.length - n; i++) {
     yield* combine(array, n - 1, i + 1, [...prev, array[i]]);
   }
 }

 const result = [...combine([1, 2, 3, 4], 2)];

要满足您的额外要求,您可以跳过已经包含的元素,例如:

   if(prev.some(el => compare(el, array[i]))) continue;