1

The problem I am trying to solve is that I want to generate an array which represents all combinations of elements in a source array.

Given an input of 2 items, there are 4 combinations (represented here as binary)

Group 1 | 0 | 1 | 0 | 1
Group 2 | 0 | 0 | 1 | 1
--------------------------
&Result | 0 | 1 | 2 | 3

This can be generalised so the number of combinations is 2(number of groups) (So 3 groups has 8 combinations, 4 has 16 etc).

So the question is; given a javascript array:

var groups = [
{
    name:"group1",
    bit: 1
},
{
    name:"group2",
    bit: 2
},
{
    name:"group3",
    bit: 4
}];

I need to generate an array where the index represents the and'ing of the bit property and the value of the array is arbitrary (further calculation - not relevant) so lets just make it an array of the group names (for the purpose of this question). This result is desirable:

var result = [
    {groups: []}, //0 
    {groups: ["group1"]}, //1
    {groups: ["group2"]},  //2
    {groups: ["group1","group2"]}, //3
    {groups: ["group3"]}, //4
    {groups: ["group1","group3"]}, //5
    {groups: ["group2","group3"]}, //6
    {groups: ["group1","group2","group3"]} //7
]

You can see in the comments there that each index in the array represents the act of and'ing the bit property from the original.

I have prepared a jsfiddle with the input and required output should it be useful in answering the question.


This is my current solution, based on mellamokb's answer but re-written in my prefered style. I was hoping there was a more elegant solution as this has a lot of iterations over the array which are unecessary. Any better solutions?

var resultCount = Math.pow(2,groups.length);
var result = [];
for(var i=0;i<resultCount;i++){
    result.push({
        groups: $.map(groups, function(e,idx){ 
           return  ((i & Math.pow(2,idx)) != 0)
               ? e.name
               : null
        })
    });
}
4
  • Does the solution need to worry about performance? What is the largest number of input groups you anticipate having? Commented Mar 31, 2014 at 20:12
  • Largest number of groups in practice is 4 (so 16 combinations). Realistically i'd err on the side of caution and expect there could be 6. Commented Mar 31, 2014 at 20:17
  • could you explain what iterations are unnecessary? i dont see how you could reduce the problem to less than (length^3) iterations if you want to enumerate all of the possibilities. Commented Mar 31, 2014 at 21:23
  • @tau - the only unnecessary combinations are reversed ones. All combinations are required. Commented Mar 31, 2014 at 22:58

3 Answers 3

4

Here is a relatively efficient solution that builds up the arrays by index:

var result = [];
var resultCount = Math.pow(2, groups.length);

for (var i = 0; i < resultCount; i++) {
    result[i] = { groups: [] };
    for (var g = 0; g < groups.length; g++) {
        if (i & groups[g].bit) result[i].groups.push(groups[g].name);
    }
}

Demo: http://jsfiddle.net/7ZsHL/9/

Sign up to request clarification or add additional context in comments.

2 Comments

This looks really good, but you can replace the iteration over the groups to calculate resultCount with var resultCount = Math.pow(2,groups.length)
@Jamiec: Good idea. I've always been a bit nervous about using Math.pow because it is a floating-point calculation.
1

I think you can do it without having to store the bit and what not.

var groups = [
    "group1",
    "group2",
    "group3",
    "group4"
];

var output = [];
for (var i = 0; i < Math.pow(2, groups.length); i++) {
    var arr = [];
    output.push(arr);
    for (var j = 0; j < groups.length; j++) {
        if (i & (Math.pow(2, j))) {
            arr.push(groups[j]);
        } else {
            arr.push(0);
        }
    }
}

1 Comment

You might want to store Math.pow(2, groups.length) into a variable, otherwise it is getting recalculated with every iteration.
1

I like what others have done, but in a situation like this, I might choose to be a little mover verbose in my code, as the intent of the algorithm is hard to communicate.

An Update Of Your Fiddle

var groups = [
    {
        name:"group1",
        bit: 1
    },
    {
        name:"group2",
        bit: 2
    },
    {
        name:"group3",
        bit: 4
    }];

function parseIterations(groups){
    var aIterations = [];
    var aBits = [];
    var iGroups = groups.length;
    var iTotalIterations = Math.pow(2, iGroups);
    for (var i = 0; i < iTotalIterations; i++) {
        var oIteration = { groups: [] };
        for (var j = 0; j < iGroups; j++) {
            if (typeof aBits[j] == 'undefined')
                aBits[j] = true;
            // while you could infer the .bit value from j, 
            // i've chosen to use your .bit value here.
            aBits[j] = (i % groups[j].bit == 0) ? !aBits[j] : aBits[j];
            if (aBits[j])
                oIteration.groups.push(groups[j].name);
        }
        aIterations[i] = oIteration;
    }
    return aIterations;
}

var result = parseIterations(groups);

1 Comment

I like the sentiment, but this still iterates multiple times so is essentially the same answer. Still +1 though. Thanks for your answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.