Combinatorial analysis with JS: Find combinations of arrays - javascript

Given the main array "arr":
let arr = [1,2,3,4,5]
And the "options" arrays:
let optArr1 = [1,3]
let optArr2 = [2,4,3]
let optArr3 = [1,4,2]
let optArr4 = [5]
let optArr5 = [2,3,4,5]
How to get all the possible combinations of "options" that actually fulfill all the numbers in the main array? The result I need is as follows:
combination1 = [optArr1, optArr2, optArr4]
combination2 = [optArr1, optArr2, optArr5]
combination3 = [optArr1, optArr2, optArr3, optArr4]
combination4 = [optArr1, optArr2, optArr3, optArr5]
combination5 = [optArr1, optArr3, optArr5]
combination6 = [optArr2, optArr3, optArr4]
combination7 = [optArr2, optArr3, optArr5]
I've run out of ideas of wether write a condition to stop trying new combinations or to actually write a function that performs the combinatorial analysis.
For clarification:
I'm not trying to get the code DONE by anyone else. Some light shed on how to tackle a combinatorial problem from a code pov is more than sufficient.

An unoptimized brute force approach by checking the values of a temporary array with the options.
As result, you get an array (here with just the indices of the options array) with all combinations for getting all values of the values array.
[
[0, 1, 2, 3],
[0, 1, 2, 4],
[0, 1, 3],
[0, 1, 4],
[0, 2, 3],
[0, 2, 4],
[0, 3, 4],
[0, 4],
[1, 2, 3],
[1, 2, 4],
[2, 3, 4],
[2, 4]
]
function getCombinations(values, options, size) {
function fork(temp, index) {
var itemSet = new Set;
temp.forEach(a => a.forEach(v => itemSet.add(v)));
if (itemSet.size === values.length) {
result.push(temp);
return;
}
if (index >= values.length) {
return;
}
fork(temp.concat([options[index]]), index + 1);
fork(temp, index + 1);
}
var result = [];
fork([], 0);
return result;
}
var values = [1, 2, 3, 4, 5],
options = [[1, 3], [2, 4, 3], [1, 4, 2], [5], [2, 3, 4, 5]];
console.log(getCombinations(values, options, 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Here you are required call concat function . Here, I have made concat method. But I am not sure if it really match . Here, I have give one example for ecombination1
ecombination1 = optArr2.concat(optArr3).concat(optArr4);

Related

How to modify value of n dimensional array element where indices are specified by an array in Javascript

I have an n-dimensional array and I want to access/modify an element in it using another array to specify the indices.
I figured out how to access a value, however I do not know how to modify the original value.
// Arbitrary values and shape
arr = [[[8, 5, 8],
[9, 9, 9],
[0, 0, 1]],
[[7, 8, 2],
[9, 8, 3],
[9, 5, 6]]];
// Arbitrary values and length
index = [1, 2, 0];
// The following finds the value of arr[1][2][0]
// Where [1][2][0] is specified by the array "index"
tmp=arr.concat();
for(i = 0; i < index.length - 1; i++){
tmp = tmp[index[i]];
}
// The correct result of 9 is returned
result = tmp[index[index.length - 1]];
How can I modify a value in the array?
Is there a better/more efficient way to access a value?
This is a classic recursive algorithm, as each step includes the same algorithm:
Pop the first index from indices.
Keep going with the array that the newly-popped index points to.
Until you get to the last element in indices - then replace the relevant element in the lowest-level array.
function getUpdatedArray(inputArray, indices, valueToReplace) {
const ans = [...inputArray];
const nextIndices = [...indices];
const currIndex = nextIndices.shift();
let newValue = valueToReplace;
if (nextIndices.length > 0) {
newValue = getUpdatedArray(
inputArray[currIndex],
nextIndices,
valueToReplace,
);
} else if (Array.isArray(inputArray[currIndex])) {
throw new Error('Indices array points an array');
}
ans.splice(currIndex, 1, newValue);
return ans;
}
const arr = [
[
[8, 5, 8],
[9, 9, 9],
[0, 0, 1]
],
[
[7, 8, 2],
[9, 8, 3],
[9, 5, 6]
]
];
const indices = [1, 2, 0];
const newArr = getUpdatedArray(arr, indices, 100)
console.log(newArr);
You can change the values in array like this,
arr[x][y][z] = value;
Does this help?
I think what you're looking for is this:
arr[index[0]][index[1]][index[2]] = value;
I'm having trouble understanding what you're attempting to do in the second part of your example.

Iterating over an Array of Arrays and returning the inner arrays with duplicate id's

I am new to javascript, and have been looking for an answer to this but can't seem to find a similar question on stackoverflow. I am trying to iterate over an array of arrays and only return the arrays that have duplicate id's. I can't use lodash or jQuery, just plain vanilla JS. So given the array below:
iceCreamArray = [
[1, Chocolate],
[1, Vanilla],
[2, Chocolate],
[3, Vanilla],
[4, Chocolate],
[4, Vanilla],
[5, Vanilla]
]
Would return:
resultArray = [
[1, Chocolate],
[1, Vanilla],
[4, Chocolate],
[4, Vanilla],
]
I was trying to do something like this:
iceCreamArray.forEach(function(flavor) {
if (flavor[0] === flavor[0].next){
resultArray.push(flavor)
}
})
var iceCreamArray = [
[1, "Chocolate"],
[1, "Vanilla"],
[2, "Chocolate"],
[3, "Vanilla"],
[4, "Chocolate"],
[4, "Vanilla"],
[5, "Vanilla"]
]
// count ids
var id_counts = {};
iceCreamArray.forEach(function(flavor) {
var id;
id = flavor[0];
if (typeof id_counts[id] === "undefined") {
id_counts[id] = 0;
}
id_counts[id] += 1;
});
// filter records for ids that occur more than once
console.log(iceCreamArray.filter(function(flavor) {
return id_counts[flavor[0]] > 1;
}));
You could count the occurence of the same id and filter then the array with the count.
var iceCreamArray = [[1, 'Chocolate'], [1, 'Vanilla'], [2, 'Chocolate'], [3, 'Vanilla'], [4, 'Chocolate'], [4, 'Vanilla'], [5, 'Vanilla']],
count = Object.create(null),
result;
iceCreamArray.forEach(([id]) => count[id] = (count[id] || 0) + 1);
result = iceCreamArray.filter(([id]) => count[id] !== 1);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to get the last array that includes a certain element?

I have an array of arrays and I want to check if there is a tie between the second elements and then return the first element of the last array that makes a tie.
for example this should return 4. (the first element in the last array that has a second element that makes a tie)
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
It is quite simple, you need to iterate over your source array, check if the given item matches the criteria, and save it to result if it does. Now if any other item does match the criteria, result's value will be overwritten with the new matching item.
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result;
optionsArray.forEach(function(item) {
if(item[1] == 10) {
result = item;
}
});
console.log(result);
You can create a simple find function that iterates the array backwards, and returns as soon as a condition callback returns true.
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
function find10(s) {
return s[1] === 10;
}
function findFromTheEnd(arr, cb) {
var l = arr.length;
while(l--) { // iterate backwards
if(cb(arr[l])){ // if the callback returns true
return arr[l]; // return the item
}
}
return null; // return null if none found
}
var result = findFromTheEnd(optionsArray, find10);
console.log(result);
You can use reduceRight() and return array.
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result = arr.reduceRight(function(r, e) {
if(e[1] == 10 && !r) r = e;
return r;
}, 0)
console.log(result)
You can also use for loop that starts from end and break on first match.
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result;
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i][1] == 10) {
result = arr[i]
break;
}
}
console.log(result)
A classic for in the reserve order with a break seems enough :
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var elementFound;
for (var i = optionsArray.length-1; i >=0; i--) {
if(optionsArray[i].item[1] == 10) {
elementFound = optionsArray[i].item[1];
break;
}
}
If elementFound is not undefined, it refers to the found array.
Rather than considering this as a multidimensional array problem, think of it as an array includes problem nested in an array search problem;
const aarr = [1, 2, 3, 4];
aarr.includes(3); // true
aarr.includes(10); // false
// and
const barr = ['hello', 'world'];
barr.find(item => item[0] === 'h'); // "hello"
barr.find(item => item[3] === 'l'); // "hello"
barr.find(item => item[1] === 'z'); // undefined
So to nest these,
const carr = [[1, 2, 3, 4], [4, 5, 6, 7]];
carr.find(arr => arr.includes(4)); // [1, 2, 3, 4]
carr.find(arr => arr.includes(6)); // [4, 5, 6, 7]
Next, we've reduced the whole problem down to "how to do this in reverse?"
You've a few options depending on how you want to implement it, but a simple way to do it is a shallow clone arr.slice() followed by a reverse arr.reverse() (we use the clone so there are no side-effects of reverse on the original array)
carr.slice().reverse().find(arr => arr.includes(4)); // [4, 5, 6, 7]
If you're working with an index, remember that you'll need to transform those too; -1 is fixed, otherwise transformed_index = arr.length - original_index - 1
Here is how you might implement the reverse of some of the Array methods
const optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
// index 0 1 2 3 4
function arrLast(arr, comparator, method = 'find', transform = x => x) {
return transform(arr.slice().reverse()[method](comparator), arr);
}
const findLast = (arr, comparator) => arrLast(arr, comparator);
const findLastIndex = (arr, comparator) => arrLast(arr, comparator, 'findIndex', (i, arr) => i === -1 ? -1 : arr.length - i - 1);
arrLast(optionsArray, arr => arr.includes(10)); // [4, 10]
findLastIndex(optionsArray, arr => arr.includes(10)); // 3
If you have to make comparisons among array items and you need to cut short once you are satisfied a while loop is ideal. Accordingly you may do as follows;
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]],
i = 0,
result;
while (arr[i][1] === arr[++i][1]);
result = arr[i-1][0]
console.log(result);

Array element not splicing into separate array - FreeCodeCamp > Chunky Monkey

I'm working through the FreeCodeCamp challenges and I'm stuck on the following one:
Our goal for this Algorithm is to split arr (first argument) into
smaller chunks of arrays with the length provided by size (second
argument). There are several green checks (objectives) our code needs
to pass in order to complete this Algorithm:
(['a', 'b', 'c', 'd'], 2) is expected to return [['a', 'b'], ['c', 'd']]
([0, 1, 2, 3, 4, 5], 3) is expected to return [[0, 1, 2], [3, 4, 5]]
([0, 1, 2, 3, 4, 5], 2) is expected to return [[0, 1], [2, 3], [4, 5]]
([0, 1, 2, 3, 4, 5], 4) is expected to return [[0, 1, 2, 3], [4, 5]]
([0, 1, 2, 3, 4, 5, 6, 7, 8], 2) is expected to return [[0, 1], [2, 3],
[4, 5], [6, 7], [8]].
Here is the code I've come up with:
function chunkArrayInGroups(arr, size) {
var newArray = [];
var holdArray = [];
for (var i = 0; i <= arr.length; i += size) {
holdArray = arr.slice(0, size);
removed = arr.splice(0, size);
newArray.push(holdArray);
}
if (arr.length !== 0) {
newArray.push(arr);
return newArray;
}
else return newArray;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
The algorithm works for all of the objectives, except for the last one. Instead of returning:
[[0, 1], [2, 3], [4, 5], [6, 7], [8]]
it's returning:
[[0, 1], [2, 3], [4, 5], [6, 7, 8]] - and I can't work out why.
Any help shedding some light on where I'm going wrong would be much appreciated!
I don't blame you for being confused, it took me a while to find the problem, too.
It started with identifying that your for loop was a bit odd - you weren't using the i variable for anything. Additionally, you were splice()-ing the original array down each iteration, so your intention was correct - to wait until there is less than size elements left in the array.
So I expressed that as a while loop instead, and voila, it works:
function chunkArrayInGroups(arr, size) {
var newArray = [];
var holdArray = [];
while (arr.length >= size) {
holdArray = arr.slice(0, size);
removed = arr.splice(0, size);
newArray.push(holdArray);
}
if (arr.length !== 0) {
newArray.push(arr);
return newArray;
}
else return newArray;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
You are recaluculating length again and again, but you forgot that you are actually removing elements from arr, so length would not be the same as size increases length decreases,either you can give as while loop or keep original length in a variable and check
function chunkArrayInGroups(arr, size) {
var newArray = [];
var holdArray = [];
var length=arr.length;
for (var i = 0; i <=length; i += size) {
holdArray = arr.slice(0, size);
removed = arr.splice(0, size);
newArray.push(holdArray);
}
if (arr.length !== 0) {
newArray.push(arr);
return newArray;
}
else return newArray;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);

How to sum elements at the same index in array of arrays into a single array?

Let's say that I have an array of arrays, like so:
[
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
How do I generate a new array that sums all of the values at each position of the inner arrays in javascript? In this case, the result would be: [17, 10, 19]. I need to be able to have a solution that works regardless of the length of the inner arrays. I think that this is possible using some combination of map and for-of, or possibly reduce, but I can't quite wrap my head around it. I've searched but can't find any examples that quite match this one.
You can use Array.prototype.reduce() in combination with Array.prototype.forEach().
var array = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
],
result = array.reduce(function (r, a) {
a.forEach(function (b, i) {
r[i] = (r[i] || 0) + b;
});
return r;
}, []);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Update, a shorter approach by taking a map for reducing the array.
var array = [[0, 1, 3], [2, 4, 6], [5, 5, 7], [10, 0, 3]],
result = array.reduce((r, a) => a.map((b, i) => (r[i] || 0) + b), []);
console.log(result);
Using Lodash 4:
function sum_columns(data) {
return _.map(_.unzip(data), _.sum);
}
var result = sum_columns([
[1, 2],
[4, 8, 16],
[32]
]);
console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
For older Lodash versions and some remarks
Lodash 4 has changed the way _.unzipWith works, now the iteratee gets all the values passed as spread arguments at once, so we cant use the reducer style _.add anymore. With Lodash 3 the following example works just fine:
function sum_columns(data) {
return _.unzipWith(data, _.add);
}
var result = sum_columns([
[1, 2],
[4, 8, 16],
[32],
]);
console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
_.unzipWith will insert undefineds where the row is shorter than the others, and _.sum treats undefined values as 0. (as of Lodash 3)
If your input data can contain undefined and null items, and you want to treat those as 0, you can use this:
function sum_columns_safe(data) {
return _.map(_.unzip(data), _.sum);
}
function sum_columns(data) {
return _.unzipWith(data, _.add);
}
console.log(sum_columns_safe([[undefined]])); // [0]
console.log(sum_columns([[undefined]])); // [undefined]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
This snipet works with Lodash 3, unfortunately I didn't find a nice way of treating undefined as 0 in Lodash 4, as now sum is changed so _.sum([undefined]) === undefined
One-liner in ES6, with map and reduce
var a = [ [0, 1, 3], [2, 4, 6], [5, 5, 7], [10, 0, 3] ];
var sum = a[0].map((_, i) => a.reduce((p, _, j) => p + a[j][i], 0));
document.write(sum);
Assuming that the nested arrays will always have the same lengths, concat and reduce can be used.
function totalIt (arr) {
var lng = arr[0].length;
return [].concat.apply([],arr) //flatten the array
.reduce( function(arr, val, ind){ //loop over and create a new array
var i = ind%lng; //get the column
arr[i] = (arr[i] || 0) + val; //update total for column
return arr; //return the updated array
}, []); //the new array used by reduce
}
var arr = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
console.log(totalIt(arr)); //[17, 10, 19]
Assuming array is static as op showned.
a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
b = []
for(i = 0; i < a[0].length; i++){
count = 0
for(j = 0; j < a.length; j++){
count += a[j][i]
}
b.push(count)
}
console.log(b)
So far, no answer using the for ... of mentioned in the question.
I've used a conditional statement for different lengths of inner arrays.
var a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
i = 0;
r = []
for (const inner of a) {
j = 0;
for (const num of inner) {
if (j == r.length) r.push(num)
else r[j] += num
j++;
}
i++;
}
console.log(r);
True, in this case, the classic for cycle fits better than for ... of.
The following snippet uses a conditional (ternary) operator.
var a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
r = [];
for (i = 0; i < a.length; i++) {
for (j = 0; j < a[i].length; j++) {
j==r.length ? r.push(a[i][j]) : r[j]+=a[i][j]
}
}
console.log(r);
A solution using maps and reductions, adding elements from different lengths of arrays.
var array = [
[0],
[2, 4],
[5, 5, 7, 10, 20, 30],
[10, 0]
];
b = Array(array.reduce((a, b) => Math.max(a, b.length), 0)).fill(0);
result = array.reduce((r, a) => b.map((_, i) => (a[i] || 0) + (r[i] || 0)), []);
console.log(result);
const ar = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
ar.map( item => item.reduce( (memo, value)=> memo+= value, 0 ) )
//result-> [4, 12, 17, 13]

Categories

Resources