I recently wrote a recursive function to create all combinations of arrays. You would have to translate your data into an array of arrays that my function uses, but that shouldn't be difficult.
Anyway, here's the code with a runnable example:
var v = [['Miniors','Kadettes','Juniors', 'Seniors'], ['Boys','Girls','Men','Women'],['54kg - 62kg','64kg - 70kg','71kg - 78kg','79kg - 84kg']];
var combos = createCombinations(v);
for(var i = 0; i < combos.length; i++) {
document.getElementsByTagName("body")[0].innerHTML += combos[i] + "<br/>";
}
function createCombinations(fields, currentCombinations) {
//prevent side-effects
var tempFields = fields.slice();
//recursively build a list combinations
var delimiter = ' | ';
if (!tempFields || tempFields.length == 0) {
return currentCombinations;
}
else {
var combinations = [];
var field = tempFields.pop();
for (var valueIndex = 0; valueIndex < field.length; valueIndex++) {
var valueName = field[valueIndex];
if (!currentCombinations || currentCombinations.length == 0) {
var combinationName = valueName;
combinations.push(combinationName);
}
else {
for (var combinationIndex = 0; combinationIndex < currentCombinations.length; combinationIndex++) {
var currentCombination = currentCombinations[combinationIndex];
var combinationName = valueName + delimiter + currentCombination;
combinations.push(combinationName);
}
}
}
return createCombinations(tempFields, combinations);
}
}
Answer from Dave on Stack OverflowI recently wrote a recursive function to create all combinations of arrays. You would have to translate your data into an array of arrays that my function uses, but that shouldn't be difficult.
Anyway, here's the code with a runnable example:
var v = [['Miniors','Kadettes','Juniors', 'Seniors'], ['Boys','Girls','Men','Women'],['54kg - 62kg','64kg - 70kg','71kg - 78kg','79kg - 84kg']];
var combos = createCombinations(v);
for(var i = 0; i < combos.length; i++) {
document.getElementsByTagName("body")[0].innerHTML += combos[i] + "<br/>";
}
function createCombinations(fields, currentCombinations) {
//prevent side-effects
var tempFields = fields.slice();
//recursively build a list combinations
var delimiter = ' | ';
if (!tempFields || tempFields.length == 0) {
return currentCombinations;
}
else {
var combinations = [];
var field = tempFields.pop();
for (var valueIndex = 0; valueIndex < field.length; valueIndex++) {
var valueName = field[valueIndex];
if (!currentCombinations || currentCombinations.length == 0) {
var combinationName = valueName;
combinations.push(combinationName);
}
else {
for (var combinationIndex = 0; combinationIndex < currentCombinations.length; combinationIndex++) {
var currentCombination = currentCombinations[combinationIndex];
var combinationName = valueName + delimiter + currentCombination;
combinations.push(combinationName);
}
}
}
return createCombinations(tempFields, combinations);
}
}
function iterate(lists, fn)
{
var values = [];
function process(listIndex)
{
var list = lists[listIndex];
// no list? create the value
if (!list)
{
fn.apply(null, values);
return;
}
for (var i = 0; i < list.length; i++)
{
values[listIndex] = list[i];
process(listIndex+1);
}
}
process(0);
}
here is a working example based on the data mentioned in your question: http://jsbin.com/boqucu/2/edit
Videos
What is a nested loop in JavaScript?
Why would I use nested loops?
How do I control the number of iterations in nested loops?
var arr = [[1,2], [3,4], [5,6]];
This is an array of arrays. It is a little bit easier to read like this:
var arr = [
[1,2],
[3,4],
[5,6]
];
That makes it a little bit easier to see that you have an array of 3 arrays. The outer 'for' will loop through each of 1st level arrays. So the very first outer for loop when i=0 you are going to grab the first inner array [1,2]:
for (var i=0; i < arr.length; i++) {
//First time through i=0 so arr[i]=[1,2];
}
In the inner loop you are going to loop through each of the 3 inner arrays one at a time.
for (var j=0; j < arr[i].length; j++) {
//Handle inner array.
}
This argument grabs the length of the inner array:
arr[i].length
So on your first time through the outer loop i=0 and arr[i] is going to equal [1,2] because you are grabbing the 0th element. Remember, arrays elements are always counted starting at 0, not 1.
Finally you are printing out the results with:
console.log(arr[i][j]);
The first time through you can break it down a little. i=0 and j=0. arr[0][0] which translates as grab the first element from the outer array and then the first element from the first inner array. In this case it is '1':
[
[1,2], <-- 0
[3,4], <-- 1
[5,6] <-- 2
];
The code will loop through the first first set [1,2], then the second [3,4], and so on.
The double for loop you have above works like so:
var arr = [[1,2], [3,4], [5,6]];
for (var i=0; i < arr.length; i++) {
// i = 0, then we loop below:
for (var j=0; j < arr[i].length; j++) {
//here we loop through the array which is in the main array
//in the first case, i = 0, j = 1, then we loop again, i = 0, j = 1
console.log(arr[i][j]);
//after we finish the stuff in the 'j' loop we go back to the 'i' loop
//and here i = 1, then we go down again, i, remains at 1, and j = 0, then j = 1
//....rinse and repeat,
}
}
In plain english:
We grab the first element in the main array, which is an array itself, we loop through that, and log at each index, this is terminated by our length condition in the second loop. We then move to to the next index of the main array, which is an array itself.... and so on, until we reach the end of the main array
To access and index in the main array, we need to use array[i] - that index holds an array - so to go INTO that array, we need to use array[i][j]
Hope that makes sense!
As I mentioned in the comments you can get the same result directly from MongoDB using a MapReduce query; However a cleaner JavaScript equivalent for your nested loops can be something like this:
var data = [
{"_id": "81724587125","name": "Object 1", "arrayOne":["1","2","3"]},
{"_id": "87687687687","name": "Object 2", "arrayOne":["4","5","6"]}
];
var result = data.reduce(function (previousValue, currentValue, currentIndex, array) {
return previousValue.arrayOne.concat( currentValue.arrayOne );
});
Nested loops are expensive when they both iterate over independent data in a "criss-cross" fashion, because they may represent a O(n^2) algorithm that can be made more efficient. You may think of such loops as describing the program "for every element in X, loop over every element in Y again".
This is not one of those cases. Your data are an array of objects, each containing further arrays of objects. A nested loop here is not only fine: it's completely expressive and appropriate.
You can use reduceRight() for this. It just starts from the inside at the last item in the keys list and works its way out starting with "cool":
let keys = ["a", "b", "c", "d"]
let limit = 3
let result = keys.reduceRight((obj, key) => ({[key]: obj}), "cool")
console.log(result)
To limit where the object stops you can iterate over a slice of the keys. For example:
let keys = ["a", "b", "c", "d"]
let start = 0
let stop = 3 // slices are don't inlcude the last item, so this will stop at index 2
let result = keys.slice(start, stop).reduceRight((obj, key) => ({
[key]: obj
}), "cool")
console.log(result)
If you like to add to a given object a new property, you could reduce the keys with the object and take default objects for not given keys. At the end assign the value.
function setValue(object, path, value, limit) {
var keys = path.slice(0, limit),
last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
return object;
}
var result = { foo: 42 },
keys = ["a", "b", "c", "d"];
setValue(result, keys, 'cool');
console.log(result);
setValue(result, keys, 'cool', 3);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The problem is that:
your
console.logshould only be executed at the deepest level. So put thatconsole.login anelseclause.The base case happens when
loopNumber === maxNestedLoops - 1as that is the last index of your array, so theifcondition should correspond to that
if (loopNumber < maxNestedLoops - 1){
looper(loopNumber + 1);
} else {
console.log(indexes);
}
There is problem with the for loop. indexes[loopNumber]++ will be increased until it will reach the value of iterations. Since your value of iterations is 3, you will end up with values which are equal to 3 in the indexes array, because the loop itself is modifying the array.
Also, since you modify your array if indexes before checking if the loopNumber reached the number of maxNestedLoops, you will end up with an array with length of maxNestedLoops + 1.
What I suggest you should do:
const maxNestedLoops = 3;
const iterations = 3;
const indexes = [];
function looper(loopNumber) {
// Check if we reached the number of nested loops before doing anything else.
if (loopNumber < maxNestedLoops) {
// Don't modify the indexes array directly, use a variable instead
for (let i = 0; i < iterations; i++) {
indexes[loopNumber] = i;
looper(loopNumber + 1);
console.log(indexes);
}
}
}
looper(0);