【问题标题】:Find all combinations of options in a loop在循环中查找所有选项组合
【发布时间】:2023-03-24 17:11:02
【问题描述】:

我正在寻找循环遍历一系列选项的最佳方法,以确保我点击了所有可用选项。

我创建了一项功能,允许客户端构建基本上是其他图像相互叠加的图像。这些其他图像被分成不同的组。他们在图片的一侧有链接,他们可以单击以滚动浏览所有不同的图片以查看它们。

现在我正在创建一个自动化流程,该流程将在用户单击其中一个链接时运行更改图像的功能。我需要确保在这个过程中,不同图像的每一个可能的组合都被击中。

假设有 3 种不同的帽子、4 种不同的衬衫、5 条不同的裤子和 6 种不同的鞋子。我可以将其表示为一个数组,其中包含每个组的选项数量。当前数组是[3, 4, 5, 6]

循环遍历此数组以确保显示所有可能选项的最佳方法是什么?

【问题讨论】:

  • 显示代码总是有帮助的。你能发布你有什么相关的代码吗?
  • 不完全确定你在问什么 - 这是一个排列问题吗?即你想要 [9,3,3,3], [3,9,3,3], [3,3,9,3], [3,3,3,9] .. 类似的东西?跨度>
  • @arxanas 我会尽快得到代码,但我现在正在家里写这个。感谢收看。
  • @Gabriel Florit 抱歉,如果我解释得不好。这样想,我有一张显示服装的图像。有 9 种不同的帽子、3 种不同的衬衫、3 条不同的裤子和 3 种不同的鞋子。我需要遍历这些,以便显示所有不同的可能服装组合。

标签: javascript arrays loops


【解决方案1】:

您想要所有数组中的Cartesian Product

我的网站上有一个讨论此问题的页面,包括 JavaScript 中的实现:
http://phrogz.net/lazy-cartesian-product

例如,要以“前进”顺序快速遍历它们,您可以使用:

hats   = ['fez','fedora']
shirts = ['t-shirt','long']
pants  = ['shorts','jeans']
shoes  = ['sneaker','loafer']

lazyProduct( [hats,shirts,pants,shoes], function(hat,shirt,pant,shoe){
  // Your function is yielded unique combinations of values from the arrays
  console.log(hat,shirt,pant,shoe);
});

function lazyProduct(sets,f,context){
  if (!context) context=this;
  var p=[],max=sets.length-1,lens=[];
  for (var i=sets.length;i--;) lens[i]=sets[i].length;
  function dive(d){
    var a=sets[d], len=lens[d];
    if (d==max) for (var i=0;i<len;++i) p[d]=a[i], f.apply(context,p);
    else        for (var i=0;i<len;++i) p[d]=a[i], dive(d+1);
    p.pop();
  }
  dive(0);
}

输出:

fez t-shirt shorts sneaker
fez t-shirt shorts loafer
fez t-shirt jeans sneaker
fez t-shirt jeans loafer
fez long shorts sneaker
fez long shorts loafer
fez long jeans sneaker
fez long jeans loafer
fedora t-shirt shorts sneaker
fedora t-shirt shorts loafer
fedora t-shirt jeans sneaker
fedora t-shirt jeans loafer
fedora long shorts sneaker
fedora long shorts loafer
fedora long jeans sneaker
fedora long jeans loafer
fez t-shirt shorts sneaker
fez t-shirt shorts loafer

这与以下结果相同:

hats.forEach(function(hat){
  shirts.forEach(function(shirt){
    pants.forEach(function(pant){
      shoes.forEach(function(shoe){
        console.log(hat,shirt,pant,shoe);
      });
    });
  });
});

或(对于旧版浏览器):

for (var h=0;h<hats.length;h++){
  var hat = hats[h];
  for (var s=0;s<shirts.length;s++){
    var shirt = shirts[s];
    for (var p=0;p<pants.length;p++){
      var pant = pants[p];
      for (var e=0;e<shoes.length;e++){
        var shoe = shoes[e];
        console.log(hat,shirt,pant,shoe);        
      }
    }
  }
}

…但它支持在运行时定义的任意数量的数组。 (如果您使用的是我网站上的第一个“惰性”实现,您可以随机挑选项目、反向迭代或在任何时候轻松停止迭代。)

【讨论】:

  • 这正是我想要的。谢谢@Phrogz。我本来打算为每个项目都做 forEach,但我觉得必须有更好的方法来做这件事。
  • 您如何在对lazyProduct 函数的反应中解决此问题:第 7:43 行:预期分配或函数调用,而是看到一个表达式 no-unused-expressions 第 8:43 行:预期分配或函数调用,而是看到一个表达式 no-unused-expressions ?
【解决方案2】:

编辑:我已经使用 jsperf here 比较了不同的方法,Phrogz 的方法显然是最快的,是这里第 3 的两倍。


如果我理解正确,您问的是计算每列数字的不同基数。您可以递归地执行此操作。

function options(opArr, fullArray){
    var i = 0, j = opArr.length;
    if(j < fullArray.length){ // if opArr doesn't have item from each group, add new group
        while(i < fullArray[j]){ // count up for this group
            newArr = opArr.slice(0); // clone opArr so we don't run into shared reference troubles, not sure if necessary
            newArr[j] = i;
            i++;
            options(newArr, fullArray); // recurse
        }
    }else{ // opArr is now a unique array of your items
        // console.log(opArr);
    }
}
options([], [3, 9, 3, 3]);

注意:这个(示例)将导致创建3 * 9 * 3 * 3 = 243 数组。这样你最终会吃掉很多内存。


另一种方法是从整数转换为数组,这样可以节省内存使用,因为您可以忘记之前计算的所有数组

function countUp(arrayOfBases, callback, self){
    var arr = arrayOfBases.reverse(), x = 1, i = arr.length,
        me = (self===undefined?this:self),
        makeArr = function(arr, x, fn, me){
        var a = arr.slice(0), n = [], i = x, j = 0, k = 0;
        while(a.length > 0){
            k = a[0];
            if(k !== 0) j = i % k, i = (i - j) / k;
            else j = 0;
            n.unshift(j);
            a.shift();
        }
        fn.call(me,n);
    };
    while (i-->0) if(arr[i] !== 0) x = x * arr[i];
    i = 0;
    while(i < x){
        makeArr(arr, i, callback, me);
        i++;
    }
}
countUp([3,9,3,3], function(a){console.log(a);});

另一种方法,类似于前面的方法,保留上次生成的数组,因此循环中的计算更少,但 init 的成本更高。

function countUp2(arrayOfBases, callback, self){
    var arr = arrayOfBases.reverse(), x = 1, i = arr.length, last = [],
        me = (self===undefined?this:self),
        addOne = function(arr, n, fn, me){
        var j = n.length, i = j - 1;
        n[i]++;
        while(j = i, i-- > 0 && n[j] >= arr[j]){
            if(arr[j] === 0) n[i] += n[j], n[j] = 0;
            else n[i]++, n[j] -= arr[j];
        }
        return fn.call(me,n.slice(0)), n;
    };
    while (i-->0){
        if(arr[i] !== 0) x = x * arr[i];
        last[i] = 0;
    }
    i = 0;
    last[last.length-1] = -1;
    while(i < x){
        last = addOne(arr, last, callback, me);
        i++;
    }
}
countUp2([3,9,3,3], function(a){console.log(a);});

所有这些方法都会输出

[0,0,0,0]
[0,0,0,1]
...
[0,8,1,2]
[0,8,2,0]
...
[2,8,2,1]
[2,8,2,2]

然后您可以根据自己的选择进行处理。

【讨论】:

    【解决方案3】:

    也许这是一个迟到的答案。但我在geeksforgeeks 找到了不同的解决方案。所以任何人都可以试试这个。

    const hats   = ['fez','fedora'];
    const shirts = ['t-shirt','long'];
    const pants  = ['shorts','jeans'];
    const shoes  = ['sneaker','loafer'];
    
    const data = [hats, shirts, pants, shoes];
    const keys = ['hats', 'shirts', 'pants', 'shoes'];
    
    
    function combination(data, keys) {
        const { length: numberOfArrays } = data;
        
        /**
         * Initialize a variable called indices
         * with the length of data array and
         * fill with zeros.
         */
        const indices = Array(numberOfArrays).fill(0);
        const result = [];
    
        while (true) {
            let obj = {};
    
            /**
             * Get the values from the inner arrays
             * with help of `indices` and make an object
             */
            for (let i = 0; i < numberOfArrays; i++) {
                obj[keys[i]] = data[i][indices[i]];
            }
            
            // Push the object into the result array
            result.push(obj);
    
            // Get the last array
            let pick = numberOfArrays - 1;
            
            /**
             * find the rightmost array that has more 
             * elements left after the current element  
             * in that array
             */
            while (pick >= 0 && (indices[pick] + 1 >= data[pick].length)) {
                pick--;
            }
    
            /**
             * No such array is found so no more  
             * combinations left 
             */
            if (pick < 0) break;
    
            /**
             * If found move to next element in that array
             */
            indices[pick]++;
    
            /**
             * for all arrays to the right of this  
             * array current index again points to  
             * first element 
             */
            for (let i = pick + 1; i < numberOfArrays; i++) {
                indices[i] = 0;
            }
    
        }
    
        return result;
    }
    
    console.log(combination(data, keys));
    .as-console-wrapper {min-height: 100%!important; top: 0}

    【讨论】:

      猜你喜欢
      • 2022-01-17
      • 2015-09-12
      • 2010-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多