以编程方式生成嵌套for循环

时间:2015-07-11 22:34:45

标签: javascript arrays for-loop closures

下划线mixin和下面的函数以两种不同的方式完成相同的事情,它们获得数组的所有对。我想知道如何创建一个函数(闭包?),它允许我传递多少“对”或我想要的数组项分组,而不是每次都嵌套for loopsrange-maps

getPairs: function(arr){
  return _.chain(_.range(arr.length))
  .map(function(setOne){
    return _.chain(_.range(arr.length))
    .map(function(setTwo){
      return [
        arr[setOne],
        arr[setTwo],
      ]
    })
    .value()
  })
  .value()
}

function getPairs(arr){
  var pairs = []
  for(var i = 0; i < arr.length; i++){
    for(var p = 0; p < arr.length; p++){
      var pair = [
        arr[i],
        arr[p],
      ]
      pairs.push()
    }
  }
  return pairs
}

2 个答案:

答案 0 :(得分:0)

由于这个问题更多的是关于算法的实现,我使用Swift来实现它:

// provide 2 parameters, an array and the size of one group of elements
// [[T]] means an array of an array of type T (placeholder for anything)
func getPair<T>(array: [T], pairCount: Int) -> [[T]] {
    if pairCount <= 1 {
        // puts each element of array in a separate array:
        // [3, 6, 7] -> [[3], [6], [7]]
        return array.map{ [$0] }
    }
    // returns a flattened array of all possible element combinations
    // flatMap: array gets mapped to all other possible combinations of one less than pairCount and gets flattened/concatenated
    // el1: element of the array which can be used in the closure
    return array.flatMap{ el1 in
        // smaller pairs are mapped to bigger ones
        // el2: element of the smaller pairs (one small pair) which can be used in the closure
        return getPair(array, pairCount - 1).map{ el2 in
            return [el1] + el2
        }
    }
}

正如你所看到的,这个递归函数在代码行方面是非常有效的,但与下面更多类似C的版本相比,它的速度是2-22(平均值:4-6),特别是对于一对bigCounts。 / p>

这是另一个版本:

// helper function which "creates" for loops and passes the indexes to a closure which takes an array of indexes
// indexes = [2, 3, 4] ==> for 4 times { for 3 times { for 2 times {} } }
// the corresponding passed indexes range from [0, 0, 0] to [2 - 1, 3 - 1, 4 - 1] so you can pass array.count directly
// therefore the index of the inner for loop can be accessed with passedArray[0] and so on
func forIndexes(indexes: [Int], closure: [Int] -> ()) {
    if indexes.count == 0 { return }

    // array which gets passed to the closure and modified over time
    var currentIndexes = Array(count: indexes.count, repeatedValue: 0)

    // maximum overall count of inner for loop: [2, 4, 5] => 2*4*5 = 40
    var count = 1
    for var i = 0; i < indexes.count; i++ {
        count *= indexes[i]
    }

    for var i = 0; i < count; i++ {
        // call closure with currentIndexes
        closure(currentIndexes)

        // increment first current index and check whether the other ones have to be updated
        if ++currentIndexes[0] == indexes[0] {
            // condition: j < indexes.count - 1 => prevent out of bounds exception for: currentIndexes[j+1]++
            for var j = 0; j < indexes.count - 1; j++ {
                if currentIndexes[j] >= indexes[j] {
                    currentIndexes[j] -= indexes[j]
                    currentIndexes[j+1]++
                }
            }
        }
    }
}

func getPair2<T>(array: [T], pairCount: Int) -> [[T]] {
    var result = [[T]]()
    // pair count determines the depth of the nested for loops while array.count specifies the loop count
    forIndexes(Array(count: pairCount, repeatedValue: array.count), closure: { indexes in
        // map array elements from all possible indexes
        result.append(indexes.map{ index in
            return array[index]
        })
    })
    return result
}

答案 1 :(得分:0)

引人入胜的问题。为了得到一个简单的解决方案,我不得不考虑一下。事实上,整个事情可以通过两个for循环和一些繁重的数学来完成。这是代码:

function getGroupings(arr, numPerGroup){
    numPerGroup > 1 || (numPerGroup = 2);
    var groups = Math.pow(arr.length, numPerGroup);
    var groupings = [];
    for (var i = 0; i < numPerGroup; i++) {
        for (var j = 0; j < groups; j++) {
            groupings[j] || groupings.push(Array(numPerGroup));
            var index = Math.floor(j / Math.pow(arr.length, i)) % arr.length;
            groupings[j][i] = arr[index];
            if (i === numPerGroup - 1) groupings[j] = groupings[j].reverse();
        }
    }
    return groupings;
}

关于这是如何工作的一些注释:

      
  • 它为内部数组中的每个项运行外部`for`循环,并为外部数组中的每个项运行内部`for`循环一次。向后,你可能会说。
  •   
  • 内部`for`循环有点像二进制时钟,其中(时钟值)===(我们想要访问的传入数组的索引)。
  •   
  • 其中 n =(传入数组的长度),它将每次在一个地方递增时钟,一个( n ^ 1)通常在( n ^ 1)的位置,一个( n ^ 2)经常出现在( n ^ 2)中的地方,依此类推,直到( n ^ num-per-group)为止。
  •   
  • 在最后一次迭代中,它反转所有内部数组以实际将最后一个放在最后,( n ^ 1)的位置倒数第二,等等......不一定必要的,但产生更多预期的产出。

示例:

假设您有一个数组var arr = [3, 6, 9],并希望获得所有可能的分组3 - getGroupings(arr, 3);。组的实际数量为arr.length ^ 3 = 27,因此该函数将生成一个包含27个数组的数组。

(忽略外部for循环 - 想象它的所有迭代都会立即发生)二进制时钟从0开始,因此第一个分组是arr[0], arr[0], arr[0] - [3, 3, 3]

在下一次迭代中,1的位置前进一个 - arr[0], arr[0], arr[1] - [3, 3, 6],然后是[3, 3, 9]

接下来是时候让3个地方前进并重置一个地方 - arr[0], arr[1], arr[0],所以分组4是[3, 6, 3]。依此类推,直到第27个数组[9, 9, 9]

这是一个JSFiddle。试试吧!