How to list all combinations in this scenario? [duplicate] - javascript

I'm trying to write an algorithm to get all the possible combinations of N elements inside a multi dimensional array of M elements.
Something like:
function getCombinations(arr, n){
...
}
var arr = [ ["A"],["B","C"],["D","E"]];
var n = 2;
getCombinations(arr,n);
This should produce:
[
["A","B"],["A","C"],["A","D"],["A","E"],
["B","D"],["B","E"],
["C","D"],["C","E"]
]
The number of elements inside the array may vary, the only thing set is the number of elements of the combinations.
The order doesn't matter but you cannot repeat, I mean ["A","B"] == ["B","A"], so the second one is not take in consideration.
Any help?

ChrisB solution had a mistake, he wasn't caching the length of the loop before the arr.shift, and it was not returning the last combination, I think this will do the job:
function getCombinations (arr, n) {
var i, j, k, elem, l = arr.length, childperm, ret = [];
if (n == 1){
for (i = 0; i < arr.length; i++) {
for (j = 0; j < arr[i].length; j++) {
ret.push([arr[i][j]]);
}
}
return ret;
}
else {
for (i = 0; i < l; i++) {
elem = arr.shift();
for (j = 0; j < elem.length; j++) {
childperm = getCombinations(arr.slice(), n-1);
for (k = 0; k < childperm.length; k++) {
ret.push([elem[j]].concat(childperm[k]));
}
}
}
return ret;
}
}
getCombinations([["A"],["B"],["C","D"]], 2);
// [["A", "B"], ["A", "C"], ["A", "D"], ["B", "C"], ["B", "D"]]
getCombinations([["A"],["B"],["C"],["D"]], 2);
// [["A", "B"], ["A", "C"], ["A", "D"], ["B", "C"], ["B", "D"], ["C", "D"]]

Updated
Per your restriction that elements that are contained in the same array in the beginning cannot be combined I've modified the algorithm to the following:
Here is the updated jsfiddle that now even outputs the data in the correct format :) http://jsfiddle.net/QKg2H/7/
function getCombinations(arr, n)
{
if(n == 1)
{
var ret = [];
for(var i = 0; i < arr.length; i++)
{
for(var j = 0; j < arr[i].length; j++)
{
ret.push([arr[i][j]]);
}
}
return ret;
}
else
{
var ret = [];
for(var i = 0; i < arr.length; i++)
{
var elem = arr.shift();
for(var j = 0; j < elem.length; j++)
{
var childperm = getCombinations(arr.slice(), n-1);
for(var k = 0; k < childperm.length; k++)
{
ret.push([elem[j]].concat(childperm[k]));
}
}
}
return ret;
}
}
The algorithm is still recursive, but now it will consider each of the second degree elements in turn, but not with each other. Other than that, it still pops off one element and then appends the permutations of all of the remaining elements. I hope it's straightforward.

Related

possible combinations get a targetTotal counting itself as a case

Trying to count possible combinations to get a targetTotal. Using powerSet returns the sum without adding itself. E.g [1,2,3,5] returns [3+1] for a targetSum of 4, whereas I expect to get [1+1+1+1], [2+2], [3+1].
Do you have any ideas how I could make it count itself first as a case?
function powerset(arr) {
var ps = [[]];
for (var i=0; i < arr.length; i++) {
for (var j = 0, len = ps.length; j < len; j++) {
ps.push(ps[j].concat(arr[i]));
}
}
return ps;
}
function sum(arr) {
var total = 0;
for (var i = 0; i < arr.length; i++)
total += arr[i];
return total
}
function findSums(numbers, targetSum) {
var sumSets = [];
var numberSets = powerset(numbers);
for (var i=0; i < numberSets.length; i++) {
var numberSet = numberSets[i];
if (sum(numberSet) == targetSum)
sumSets.push(numberSet);
}
return sumSets;
}
Example invocation:
findSums([1,2,3,4,5],6); [[2,3], [1,4], [5], [1,1,1,1,1,1], [2,2,2], [3,3]]

Arrays with elements less or equal to the elements in given array

What is the most JS-style way to solve the following problem?
Given an array A, find all arrays B, such that for i <= A.length: B[i] <= A[i]. Example of what I expect:
#Input
A = [1,2,0]
#Output
B = [[0,0,0],
[1,0,0],
[1,1,0],
[1,2,0],
[0,1,0],
[0,2,0]]
In Python I used:
B = [[]];
for t in [range(e+1) for e in A]:
B = [x+[y] for x in B for y in t]
Thanks in advance!
Use the following code (any loop for one item of the array a):
var a = [1, 2, 0], b = [];
for (var i = 0; i < a[0]; i++) {
for (var j = 0; j < a[1]; j++) {
for (var k = 0; k <= a[2]; k++) {
b.push([i, j, k]);
}
}
}
If you know the numebr of items in the array a only on runtime, use the following recursive function:
function fillArray(source, dest, recursionLevel, tempArr) {
if (recursionLevel >= source.length) {
dest.push(tempArr);
return;
}
for (var i = 0; i <= source[recursionLevel]; i++) {
var tempArr2 = tempArr.slice(); // Copy tempArr
tempArr2.push(i);
fillArray(source, dest, recursionLevel + 1, tempArr2);
}
}
fillArray(a, b, 0, []);
I found this solution. I'm sure it can be coded in a much nicer way. However, it works and I hope you find it useful
all_combinations(A){
var B = [];
for (var i = 0; i < A[0] + 1; i++) {
B.push([i]);
}
for (var i = 1; i < A.length; i++) {
var _tmp_array = [];
for (var j = 0; j < A[i] + 1; j++) {
for (var k = 0; k < B.length; k++) {
var _new_element = B[k].concat([j]);
_tmp_array.push(_new_element);
}
}
B = _tmp_array;
}
return B;
}

Why does this return [['a', 'b']], instead of [['a','b'],['c','d']]?

I am doing freecodecamp's Bonfire:Chunky Monkey. I almost have the solution, but I can't figure it out why it isn't working. So my question is: "Why does this return [['a', 'b']], instead of [['a','b'],['c','d']]?
function chunk(arr, size) {
var array = [];
var tmp = [];
for(var i = 0; i < Math.floor(arr.length/size); i++)
{
for(var j = 0; j < size; j++)
{
tmp.push(arr[j]);
}
array.push(tmp);
tmp = [];
arr.splice(0,size);
}
return array;
}
chunk(['a', 'b', 'c', 'd'], 2);
Because you are altering the length of arr within the loop. As a result, the outer loop only runs once. You need to cache this before you alter it:
function chunk(arr, size) {
var array = [];
var tmp = [];
// save this, otherwise the 2nd iteration will not run at all
// because the new array length will be 2, making half of that 1
var iter = Math.floor(arr.length / size);
for (var i = 0; i < iter; i++) {
for (var j = 0; j < size; j++) {
tmp.push(arr[j]);
}
array.push(tmp);
tmp = [];
arr.splice(0, size);
}
return array;
}
You are modifying the length of arr on each iteration preventing it from executing the second time.
Besides, one loop is enough.
function chunk(arr, size) {
var array = [];
for(var i = 0; i < arr.length; i += size) {
array.push(arr.slice(i, i + size));
}
return array;
}
chunk(['a', 'b', 'c', 'd'], 2);
Another approach:
function chunk(arr, size) {
var array = [];
var tmp = [];
var aux = 0;
for(var i = 0; i < Math.ceil(arr.length/size); i++)
{
for(var j = aux; j < aux + size; j++)
{
arr[j] != undefined?tmp.push(arr[j]):false;
}
aux = aux + size;
array.push(tmp);
tmp = [];
}
return array;
}
console.log(chunk(['a', 'b', 'c', 'd', 'e', 'f'], 2));
PS: It works with even and odd number of elements in the array.
Plenty of answers with working codes, so I just answer the why.
You thought the outer loop iterates twice, because Math.floor(arr.length/size) is 2 at the beginning:
for(var i = 0; i < Math.floor(arr.length/size); i++) {
// ....
}
However, arr is chunked in the first iteration:
arr.splice(0,size); // arr is ['c', 'd'] after this step
For the second iteration, i becomes 1 and Math.floor(arr.length/size) is actually Math.floor(['c', 'd']/2), the check fails and the loop exits. So there isn't a second iteration.
Configurable chunk size example with a loop.
function chunk(arr, chunkSize) {
var array = [];
for (var index = 0, arrLen; index < chunkSize; index++) {
arrLen = arr.length;
if (arrLen >= chunkSize) {
array[index] = arr.splice(0, chunkSize === 1 ? arrLen : chunkSize);
} else if (arrLen > 0) {
array[index] = arr.splice(0, arrLen);
}
}
return array;
}
var result = chunk(['a', 'b', 'c', 'd'], 1);
console.log(result);

How to merging javascript arrays and order by position?

Is there anyway to merge arrays in javascript by ordering by index/position. I'm try to accomplish this and haven't been able to find any examples of this.
var array1 = [1,2,3,4]
var array2 = [a,b,c,d]
var array3 = [!,#,#,$]
var merged array = [1,a,!,2,b,#,3,c,#,4,d,$]
I know you can use concat() to put one after the other.
As long as the arrays are all the same length you could just do:
var mergedArray = [];
for (var i = 0, il = array1.length; i < il; i++) {
mergedArray.push(array1[i]);
mergedArray.push(array2[i]);
mergedArray.push(array3[i]);
}
EDIT:
For arrays of varying lengths you could do:
var mergedArray = [];
for (var i = 0, il = Math.max(array1.length, array2.length, array3.length);
i < il; i++) {
if (array1[i]) { mergedArray.push(array1[i]); }
if (array2[i]) { mergedArray.push(array2[i]); }
if (array3[i]) { mergedArray.push(array3[i]); }
}
This should work for arrays of ANY length:
var mergeArrays = function () {
var arr = [],
args = arr.slice.call(arguments),
length = 0;
for (var i = 0, len = args.length; i < len; i++) {
length = args[i].length > length ? args[i].length : length;
}
for (i = 0; i < length; i++) {
for (var j = 0; j < len; j++) {
var value = args[j][i];
if (value) {
arr.push(value);
}
}
}
return arr;
};
Example:
var array1 = [1,2,3,4];
var array2 = ['a','b','c','d','e','f','g','h','i','j','k','l'];
var array3 = ['!','#','#','$','%','^','&','*','('];
mergeArrays(array1, array2, array3);
// outputs: [1, "a", "!", 2, "b", "#", 3, "c", "#", 4, "d", "$", "e", "%", "f", "^", "g", "&", "h", "*", "i", "(", "j", "k", "l"]
This would work also (a little more terse syntax):
var mergeArrays = function () {
var arr = [],
args = arr.slice.call(arguments),
length = Math.max.apply(null, args.map(function (a) { return a.length; }));
for (i = 0; i < length; i++) {
for (var j = 0, len = args.length; j < len; j++) {
var value = args[j][i];
if (value) {
arr.push(value);
}
}
}
return arr;
};
For arrays that are all the same size, where you pass one or more arrays as parameters to merge:
function merge()
{
var result = [];
for (var i=0; i<arguments[0].length; i++)
{
for (var j=0; j<arguments.length; j++)
{
result.push(arguments[j][i]);
}
}
return result;
}
var array1 = ['1','2','3','4'];
var array2 = ['a','b','c','d'];
var array3 = ['!','#','#','$'];
var merged = merge(array1, array2, array3);
Nothing built in, but it wouldn't be hard to manage:
var maxLength = Math.max(array1.length, array2.length, array3.length),
output = [];
for (var i = 0; i < maxLength; i++) {
if (array1[i] != undefined) output.push(array1[i]);
if (array2[i] != undefined) output.push(array2[i]);
if (array3[i] != undefined) output.push(array3[i]);
}
try this...
var masterList = new Array();
var array1 = [1,2,3,4];
var array2 = [a,b,c,d];
var array3 = [!,#,#,$];
for(i = 0; i < array1.length; i++) {
masterList.push(array1[i]);
masterList.push(array2[i]);
masterList.push(array3[i]);
}
It looks like you want to "zip" some number of same-length arrays into a single array:
var zip = function() {
var numArrays=arguments.length
, len=arguments[0].length
, arr=[], i, j;
for (i=0; i<len; i++) {
for (j=0; j<numArrays; j++) {
arr.push(arguments[j][i]);
}
}
return arr;
};
zip([1,2], ['a', 'b']); // => [1, 'a', 2, 'b']
zip([1,2,3], ['a','b','c'], ['!','#','#']); // => [1,'a','#',...,3,'c','#']
If the input arrays could be of different length then you've got to figure out how to deal with that case...
Yes, there is some way to do that. Just:
loop through the larger array,
until at the currently processed position both arrays have elements, assign them one-by-one to the new array,
after the shorter array ends, assign only elements from the longer array,
The resulting array will have the elements ordered by the index from the original arrays. From your decision depends, position in which one of these arrays will have higher priority.
This works for any number of array and with arrays of any length.
function myMerge() {
var result = [],
maxLength = 0;
for (var i = 0; i < arguments.length; i++) {
if (arguments[i].length > maxLength) { maxLength = arguments[i].length; }
}
for (var i = 0; i < maxLength; i++) {
for (var j = 0; j < arguments.length; j++) {
if (arguments[j].length > i) {
result.push(arguments[j][i]);
}
}
}
return result;
}
Eli beat me to the punch up there.
var posConcat = function() {
var arrays = Array.prototype.slice.call(arguments, 0),
newArray = [];
while(arrays.some(notEmpty)) {
for(var i = 0; i < arrays.length; i++) {
if(arguments[i].length > 0)
newArray.push(arguments[i].shift());
}
}
return newArray;
},
notEmpty = function() { return arguments[0].length > 0; };
Usage:
var orderedArray = posConcat(array1,array2,array3);
Sample: http://jsfiddle.net/HH9SR/

Joining each item of an array with the items of another array

Consider this:
[ ["a", "b"], ["c", "d"], ["e"] ]
How can this be tranformed to:
[ "a c e", "a d e", "b c e", "b d e" ]
// edit: tested and works
function product(set) {
if(set.length < 2)
return set[0];
var head = set.shift(), p = product(set), r = [];
for(var j = 0; j < head.length; j++)
for(var i = 0; i < p.length; i++)
r.push([head[j]].concat(p[i]));
return r;
}
var set = [
[ "a", "b", "c"],
[ "D", "E" ],
[ "x" ]
];
var p = product(set);
for(var i = 0; i < p.length; i++)
document.write(p[i] + "<br>");
This works:
<html><body><script>
var to_join = [ ["a", "b"], ["c", "d"], ["e"] ];
var joined = to_join[0];
for (var i = 1; i < to_join.length; i++) {
var next = new Array ();
var ends = to_join[i];
for (var j = 0; j < ends.length; j++) {
for (var k = 0; k < joined.length; k++) {
next.push (joined[k]+ " " + (ends[j]));
}
}
joined = next;
}
alert (joined);
</script></body></html>
Try concat method:
var newArr=[];
for(var i=0; i< arr.length; i++)
{
newArr = newArr.concat(arr[i]);
}

Categories