Related
I'm building a mastermind game. In this game the player needs to guess a secret random number. To make this work I need to have a function that can compare the two arrays and check if there is a match in number and position and/or a match only in the number but not the position.
The problem: This function works well for the most part when there are not repeated numbers in the arrays, but gives me a wrong output when there are repeated numbers.
example:
Random -- arr1 = ['5', '5', '3', '4']
Guess -- arr 2 = ['5', '1', '0', '0,]
expected result
{match: true, exactMatches: 1, matchesByValue: 0}
I'm getting this wrong result:
{match: true, exactMatches: 1, matchesByValue: 1}
Important: matchesByValue means correct number in wrong position. It shouldn't consider numbers that have been already counted as exactMatches.
function compareGuessVsRandom(arr1, arr2) {
// convert the arrays to sets
const set1 = new Set(arr1);
const set2 = new Set(arr2);
let exactMatches = 0;
let matchesByValue = 0;
// check if each value in the first array has the same value and position in the second array
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] == arr2[i]) {
exactMatches++;
} else if (set1.has(arr2[i])) {
matchesByValue++;
}
}
// if all checks pass, the arrays are a match
const result = {
match: true,
exactMatches: exactMatches,
matchesByValue: matchesByValue,
};
console.log(result);
return result;
}
function compareGuessVsRandom (arr1, arr2) {
// The list of all the indicies at which there is an exact match
const exactMatchesList = []
// The amount of matche by value
let matchesByValue = 0
for (let i = 0; i < arr1.length; i++)
if (arr1[i] === arr2[i])
exactMatchesList.push(i)
// Init sets with arrays without the exactly-matched values
const set1 = new Set(arr1.filter((_, i) => !exactMatchesList.includes(i)))
const set2 = new Set(arr2.filter((_, i) => !exactMatchesList.includes(i)))
// If set2 contains an element of set1, increment matchesByValue
for (const e of set1)
matchesByValue += set2.has(e)
// Get the amount of exact matches
const exactMatches = exactMatchesList.length
const result = {
match: exactMatches !== 0 || matchesByValue !== 0,
exactMatches,
matchesByValue
}
return result;
}
const random = [ '5', '5', '3', '4' ]
const guess = [ '5', '1', '0', '0' ]
console.log(compareGuessVsRandom(random, guess))
The has method returns true if set1 contains the specified element. So regardless of whether an exact match has occurred or not, it will increment 'matchesByValue' even for a match at the index with the exact match.
For example, in a scenario where arr2 has [5,5,0,0]. The first '5'. will increment 'exactMatches' but the second one will also increment 'matchesByValue' by matching it with index 0 of set1.
You can eliminate duplicate matches by comparing indexes of the two types of matches.
This function solved the problem with duplicates. Thanks everyone!
//helper function to count how many values two arrays have in common
const countCommonValuesOfArrays = (arr1, arr2) =>
arr1.reduce((a, c) => a + arr2.includes(c), 0);
export function compareGuessVsRandom(guesses, random) {
//check for duplicates
let perfectMatch = [];
let equalValues = [];
let unmatchedGuesses = [];
let unmatchedRandom = [];
for (let i = 0; i < guesses.length; i++) {
if (guesses[i] === random[i]) {
perfectMatch++;
} else {
unmatchedGuesses.push(guesses[i]);
unmatchedRandom.push(random[i]);
}
}
equalValues = countCommonValuesOfArrays(unmatchedGuesses, unmatchedRandom);
return {
gameMatch: perfectMatch === random.length,
perfectMatch: perfectMatch,
equalValues: equalValues,
};
}
Problem
Currently, I have two arrays (but the overall amount is dynamic, not necessarily always 2) and I need a way to sum each index of each array (as in, index 0 of array 1 + index 0 of array 2, index 1 of array 1 + index 1 of array 2 and so on..) and then store the collection of sums computed into its own array.
What the Arrays Look Like
[ 0.9716351, 1.116535, 1.316175 ] -- Array 1
[ 0.937825, 1.09963, 1.358035 ] -- Array 2
What I'm Trying to Do
I'm trying to add the indexes of Array 1 and 2 together, and store the sum of each index into a new "sum" array. As in:
sum = [0.9716351 + 0.937825, 1.116535+1.09963, 1.316175+1.358035] and so on.
I've tried mapping over the array's and summing them but it's not summing the correct indexes and storing them in a new array entirely.
Any help?
Use nested forEach, so that it work for dynamic number of arrays.
const data = [
[0.9716351, 1.116535, 1.316175],
[0.937825, 1.09963, 1.358035],
];
const res = [];
data.forEach((arr) => {
arr.forEach((item, index) => {
res[index] = (res[index] ?? 0) + item;
});
});
console.log(res);
This method will work even for any number of arrays that have any length ( They can be different)!
let arrays = [
[0.9716351, 1.116535, 1.316175],
[0.937825, 1.09963, 1.358035]
]
let sum = [];
let arrLengths = [];
for (array of arrays) {
arrLengths.push(array.length)
}
let gLength = Math.max(...arrLengths)
for (let i = 0; i < gLength; i++) {
let thisSum = 0
for (let p = 0; p < arrays.length; p++) {
thisSum += arrays[p][i] || 0;
}
sum[i] = thisSum
}
console.log(sum)
I have n number of arrays structured like
[{val_1: 2}, {val_1: 3}];
[{val_2: 2}];
[{val_3: 1}];
etc.
I want to create a single array structured like
[{val_1: 2, val_2: 2, val_3: 1}, {val_1: 3, val_2: 2, val_3: 1}]
in which all arrays are compared and every single possible output is produced. In the below code I have 3 arrays. One with 4 values, 2 values, and 3 values. I want it to return 24 different arrays showing all possible combinations.
var val_1 = 3
var val_2 = 2
var val_3 = 1
var total_val = 3;
var useable = 50;
var array_1 = new Array();
var array_2 = new Array();
var array_3 = new Array();
var array_1_c = new Array();
var array_2_c = new Array();
var array_3_c = new Array();
for(val_1; val_1 <= total_val && val_1 <= useable; val_1+=1){
array_1 = [{val_1}];
array_1.map(test =>{
return{val_1}
}).forEach(test => array_1_c.push(test));
};
var val_1 = 2
for(val_1; val_1 >= 0; val_1-=1){
array_1 = [{val_1}];
array_1.map(test =>{
return{val_1}
}).forEach(test => array_1_c.push(test));
};
for(val_2; val_2 <= total_val && val_2 <= useable; val_2+=1){
array_2 = [{val_2}];
array_2.map(test =>{
return{val_2}
}).forEach(test => array_2_c.push(test));
};
for(val_3; val_3 <= total_val && val_3 <= useable; val_3+=1){
array_3 = [{val_3}];
array_3.map(test =>{
return{val_3}
}).forEach(test => array_3_c.push(test));
}; console.log(array_1_c);console.log(array_2_c);console.log(array_3_c);
how would something like this be accomplished? The permute function seems to be the way to go, but I can't find a way to output every single combination. If creating multiple million combinations should I go a different route than using an object array.
Thanks!
You could take an algorithm for a cartesian product by using arrays of objects.
The result is an array of object with all properties of the given data.
const
getCartesian = array => array.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => ({ ...v, ...w }))), [])),
all = [[{ val_1: 2 }, { val_1: 3 }], [{ val_2: 2 }], [{ val_3: 1 }]],
cartesian = getCartesian(all);
console.log(cartesian);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I came across with a weird requirement and I am struggling for last few hours to complete it. Below is my Array of string(just an example, the actual array contains around 2500 records):
var testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
We have 3 element here of which each element is comma separated(each element have 6 item). i.e:
testArray[0] = "130,839.9,855,837.3,848.65,3980489"
My problem is, I wanted to sort testArray based on the first item of each element and convert it to array of array having all value into float, so the output would be:
[
[129, 875, 875, 828.1, 833.25, 6926078],
[130, 839.9, 855, 837.3, 848.65, 3980489],
[138, 891.3, 893.3, 865.2, 868.75, 5035618]
]
I am able to sort individual item but not the entire array as a whole, and I have tried using split and then sort with no luck.
Can someone help me out with this and please let me know if I am not clear.
Convert the array using Array#map within an Array#map, then use Array#sort on the converted array according to the [0] indices (a[0] - b[0]):
In ES5
var testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
var converted = testArray.map(function (item) {
return item.split(',').map(function (num) {
return parseFloat(num);
});
})
console.log(converted)
var sorted = converted.sort(function (a, b) { return a[0] - b[0] })
console.log(sorted)
In ES6
const testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
const converted = testArray.map(
item => item.split(',').map(
num => parseFloat(num)
)
)
console.log(converted)
const sorted = converted.sort((a, b) => a[0] - b[0])
console.log(sorted)
In ES6 (condensed)
const testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
const convertedAndSorted = testArray
.map(n => n.split(',')
.map(num => parseFloat(num)))
.sort((a, b) => a[0] - b[0])
console.log(convertedAndSorted)
Just map the splitted and to number formatted values and sort by the first item.
var data = ["130,839.9,855,837.3,848.65,3980489", "129,875,875,828.1,833.25,6926078", "138,891.3,893.3,865.2,868.75,5035618"],
result = data
.map(s => s.split(',').map(Number))
.sort((a, b) => a[0] - b[0]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var testArray = ["130,839.9,855,837.3,848.65,3980489","129,875,875,828.1,833.25,6926078","138,891.3,893.3,865.2,868.75,5035618"];
const output = [];
for (let i = 0; i < testArray.length; i++) {
var numbers = testArray[i].split(',');
for (let j = 0; j < numbers.length; j++) {
numbers[j] = +numbers[j];
}
output[i] = numbers;
}
output.sort(function(x, y) {
return x[0] - y[0];
});
or shorter
output = testArray.map(s => s.split(',')).map(e => e.map(n => +n)).sort((x, y) => x[0] - y[0]);
First convert each of the Strings to an array of floats values using Array.map() and parseFloat().
After that you can simply sort the array of arrays using Arrays.sort()
Try the following :
var arr = ["130,839.9,855,837.3,848.65,3980489","129,875,875,828.1,833.25,6926078","138,891.3,893.3,865.2,868.75,5035618"];
var result = arr.map((a)=> a.split(",").map((b)=>parseFloat(b))).sort((a,b)=> a[0] -b[0]);
console.log(result);
I've seen several similar questions about how to generate all possible combinations of elements in an array. But I'm having a very hard time figuring out how to write an algorithm that will only output combination pairs. Any suggestions would be super appreciated!
Starting with the following array (with N elements):
var array = ["apple", "banana", "lemon", "mango"];
And getting the following result:
var result = [
"apple banana"
"apple lemon"
"apple mango"
"banana lemon"
"banana mango"
"lemon mango"
];
I was trying out the following approach but this results in all possible combinations, instead only combination pairs.
var letters = splSentences;
var combi = [];
var temp= "";
var letLen = Math.pow(2, letters.length);
for (var i = 0; i < letLen ; i++){
temp= "";
for (var j=0;j<letters.length;j++) {
if ((i & Math.pow(2,j))){
temp += letters[j]+ " "
}
}
if (temp !== "") {
combi.push(temp);
}
}
Here are some functional programming solutions:
Using EcmaScript2019's flatMap:
var array = ["apple", "banana", "lemon", "mango"];
var result = array.flatMap(
(v, i) => array.slice(i+1).map( w => v + ' ' + w )
);
console.log(result);
Before the introduction of flatMap (my answer in 2017), you would go for reduce or [].concat(...) in order to flatten the array:
var array = ["apple", "banana", "lemon", "mango"];
var result = array.reduce( (acc, v, i) =>
acc.concat(array.slice(i+1).map( w => v + ' ' + w )),
[]);
console.log(result);
Or:
var array = ["apple", "banana", "lemon", "mango"];
var result = [].concat(...array.map(
(v, i) => array.slice(i+1).map( w => v + ' ' + w ))
);
console.log(result);
A simple way would be to do a double for loop over the array where you skip the first i elements in the second loop.
let array = ["apple", "banana", "lemon", "mango"];
let results = [];
// Since you only want pairs, there's no reason
// to iterate over the last element directly
for (let i = 0; i < array.length - 1; i++) {
// This is where you'll capture that last value
for (let j = i + 1; j < array.length; j++) {
results.push(`${array[i]} ${array[j]}`);
}
}
console.log(results);
Rewritten with ES5:
var array = ["apple", "banana", "lemon", "mango"];
var results = [];
// Since you only want pairs, there's no reason
// to iterate over the last element directly
for (var i = 0; i < array.length - 1; i++) {
// This is where you'll capture that last value
for (var j = i + 1; j < array.length; j++) {
results.push(array[i] + ' ' + array[j]);
}
}
console.log(results);
In my case, I wanted to get the combinations as follows, based on the size range of the array:
function getCombinations(valuesArray: String[])
{
var combi = [];
var temp = [];
var slent = Math.pow(2, valuesArray.length);
for (var i = 0; i < slent; i++)
{
temp = [];
for (var j = 0; j < valuesArray.length; j++)
{
if ((i & Math.pow(2, j)))
{
temp.push(valuesArray[j]);
}
}
if (temp.length > 0)
{
combi.push(temp);
}
}
combi.sort((a, b) => a.length - b.length);
console.log(combi.join("\n"));
return combi;
}
Example:
// variable "results" stores an array with arrays string type
let results = getCombinations(['apple', 'banana', 'lemon', ',mango']);
Output in console:
The function is based on the logic of the following documentation, more information in the following reference:
https://www.w3resource.com/javascript-exercises/javascript-function-exercise-3.php
if ((i & Math.pow(2, j)))
Each bit of the first value is compared with the second, it is taken as valid if it matches, otherwise it returns zero and the condition is not met.
Although solutions have been found, I post here an algorithm for general case to find all combinations size n of m (m>n) elements. In your case, we have n=2 and m=4.
const result = [];
result.length = 2; //n=2
function combine(input, len, start) {
if(len === 0) {
console.log( result.join(" ") ); //process here the result
return;
}
for (let i = start; i <= input.length - len; i++) {
result[result.length - len] = input[i];
combine(input, len-1, i+1 );
}
}
const array = ["apple", "banana", "lemon", "mango"];
combine( array, result.length, 0);
I ended up writing a general solution to this problem, which is functionally equivalent to nhnghia's answer, but I'm sharing it here as I think it's easier to read/follow and is also full of comments describing the algorithm.
/**
* Generate all combinations of an array.
* #param {Array} sourceArray - Array of input elements.
* #param {number} comboLength - Desired length of combinations.
* #return {Array} Array of combination arrays.
*/
function generateCombinations(sourceArray, comboLength) {
const sourceLength = sourceArray.length;
if (comboLength > sourceLength) return [];
const combos = []; // Stores valid combinations as they are generated.
// Accepts a partial combination, an index into sourceArray,
// and the number of elements required to be added to create a full-length combination.
// Called recursively to build combinations, adding subsequent elements at each call depth.
const makeNextCombos = (workingCombo, currentIndex, remainingCount) => {
const oneAwayFromComboLength = remainingCount == 1;
// For each element that remaines to be added to the working combination.
for (let sourceIndex = currentIndex; sourceIndex < sourceLength; sourceIndex++) {
// Get next (possibly partial) combination.
const next = [ ...workingCombo, sourceArray[sourceIndex] ];
if (oneAwayFromComboLength) {
// Combo of right length found, save it.
combos.push(next);
}
else {
// Otherwise go deeper to add more elements to the current partial combination.
makeNextCombos(next, sourceIndex + 1, remainingCount - 1);
}
}
}
makeNextCombos([], 0, comboLength);
return combos;
}
The best solutions I have found - https://lowrey.me/es6-javascript-combination-generator/
Uses ES6 generator functions, I adapted to TS. Most often you don't need all of the combinations at the same time. And I was getting annoyed by writing loops like for (let i=0; ... for let (j=i+1; ... for (let k=j+1... just to get combos one by one to test if I need to terminate the loops..
export function* combinations<T>(array: T[], length: number): IterableIterator<T[]> {
for (let i = 0; i < array.length; i++) {
if (length === 1) {
yield [array[i]];
} else {
const remaining = combinations(array.slice(i + 1, array.length), length - 1);
for (let next of remaining) {
yield [array[i], ...next];
}
}
}
}
usage:
for (const combo of combinations([1,2,3], 2)) {
console.log(combo)
}
output:
> (2) [1, 2]
> (2) [1, 3]
> (2) [2, 3]
Just to give an option for next who'll search it
const arr = ['a', 'b', 'c']
const combinations = ([head, ...tail]) => tail.length > 0 ? [...tail.map(tailValue => [head, tailValue]), ...combinations(tail)] : []
console.log(combinations(arr)) //[ [ 'a', 'b' ], [ 'a', 'c' ], [ 'b', 'c' ] ]
There are also this answer:
https://stackoverflow.com/a/64414875/19518308
The alghorithm is this answer generates all the possible sets of combination(or choose(n, k)) of n items within k spaces.
The algorhitm:
function choose(arr, k, prefix=[]) {
if (k == 0) return [prefix];
return arr.flatMap((v, i) =>
choose(arr.slice(i+1), k-1, [...prefix, v])
);
}
console.log(choose([0,1,2,3,4], 3));
I had a similar problem and this algorhitm is working very well for me.
Using map and flatMap the following can be done (flatMap is only supported on chrome and firefox)
var array = ["apple", "banana", "lemon", "mango"]
array.flatMap(x => array.map(y => x !== y ? x + ' ' + y : null)).filter(x => x)
I think it is an answer to all such questions.
/**
*
* Generates all combination of given Array or number
*
* #param {Array | number} item - Item accepts array or number. If it is array exports all combination of items. If it is a number export all combination of the number
* #param {number} n - pow of the item, if given value is `n` it will be export max `n` item combination
* #param {boolean} filter - if it is true it will just export items which have got n items length. Otherwise export all posible length.
* #return {Array} Array of combination arrays.
*
* Usage Example:
*
* console.log(combination(['A', 'B', 'C', 'D'], 2, true)); // [[ 'A','A' ], [ 'A', 'B' ]...] (16 items)
* console.log(combination(['A', 'B', 'C', 'D'])); // [['A', 'A', 'A', 'B' ],.....,['A'],] (340 items)
* console.log(comination(4, 2)); // all posible values [[ 0 ], [ 1 ], [ 2 ], [ 3 ], [ 0, 0 ], [ 0, 1 ], [ 0, 2 ]...] (20 items)
*/
function combination(item, n) {
const filter = typeof n !=='undefined';
n = n ? n : item.length;
const result = [];
const isArray = item.constructor.name === 'Array';
const count = isArray ? item.length : item;
const pow = (x, n, m = []) => {
if (n > 0) {
for (var i = 0; i < count; i++) {
const value = pow(x, n - 1, [...m, isArray ? item[i] : i]);
result.push(value);
}
}
return m;
}
pow(isArray ? item.length : item, n);
return filter ? result.filter(item => item.length == n) : result;
}
console.log("#####first sample: ", combination(['A', 'B', 'C', 'D'], 2)); // with filter
console.log("#####second sample: ", combination(['A', 'B', 'C', 'D'])); // without filter
console.log("#####third sample: ", combination(4, 2)); // gives array with index number
Generating combinations of elements in an array is a lot like counting in a numeral system,
where the base is the number of elements in your array (if you account for the leading zeros that will be missing).
This gives you all the indices to your array (concatenated):
arr = ["apple", "banana", "lemon", "mango"]
base = arr.length
idx = [...Array(Math.pow(base, base)).keys()].map(x => x.toString(base))
You are only interested in pairs of two, so restrict the range accordingly:
range = (from, to) = [...Array(to).keys()].map(el => el + from)
indices = range => range.map(x => x.toString(base).padStart(2,"0"))
indices( range( 0, Math.pow(base, 2))) // range starts at 0, single digits are zero-padded.
Now what's left to do is map indices to values.
As you don't want elements paired with themselves and order doesn't matter,
those need to be removed, before mapping to the final result.
const range = (from, to) => [...Array(to).keys()].map(el => el + from)
const combinations = arr => {
const base = arr.length
return range(0, Math.pow(base, 2))
.map(x => x.toString(base).padStart(2, "0"))
.filter(i => !i.match(/(\d)\1/) && i === i.split('').sort().join(''))
.map(i => arr[i[0]] + " " + arr[i[1]])
}
console.log(combinations(["apple", "banana", "lemon", "mango"]))
With more than ten elements, toString() will return letters for indices; also, this will only work with up to 36 Elements.
Generating combinations is a classic problem. Here's my interpretation of that solution:
const combinations = (elements) => {
if (elements.length == 1) {
return [elements];
} else {
const tail = combinations(elements.slice(1));
return tail.reduce(
(combos, combo) => { combos.push([elements[0], ...combo]); return combos; },
[[elements[0]], ...tail]
);
}
};
const array = ["apple", "banana", "lemon", "mango"];
console.log(combinations(array));
Here is an non-mutating ES6 approach combining things (TS):
function combine (tail: any[], length: number, head: any[][] = [[]]): any[][] {
return tail.reduce((acc, tailElement) => {
const tailHeadVariants = head.reduce((acc, headElement: any[]) => {
const combination = [...headElement, tailElement]
return [...acc, combination]
}, [])
if (length === 1) return [...acc, tailHeadVariants]
const subCombinations = combine(tail.filter(t => t !== tailElement), length - 1, tailHeadVariants)
return [...acc, ...subCombinations]
}, [])
}
As this post is well indexed on Google under the keywords "generate all combinations", lots of people coming here simply need to generate all the unique combinations, regardless of the size of the output (not only pairs).
This post answers this need.
All unique combinations, without recursion:
const getCombos = async (a) => {
const separator = '';
const o = Object();
for (let i = 0; i < a.length; ++i) {
for (let j = i + 1; j <= a.length; ++j) {
const left = a.slice(i, j);
const right = a.slice(j, a.length);
o[left.join(separator)] = 1;
for (let k = 0; k < right.length; ++k) {
o[[...left, right[k]].join(separator)] = 1;
}
}
}
return Object.keys(o);
}
const a = ['a', 'b', 'c', 'd'];
const b = await getCombos(a);
console.log(b);
// (14) ['a', 'ab', 'ac', 'ad', 'abc', 'abd', 'abcd',
// 'b', 'bc', 'bd', 'bcd', 'c', 'cd', 'd']
This code splits the array into 2 sub arrays, left / right, then iterate over the right array to combine it with the left array. The left becomes bigger overtime, while the right becomes smaller. The result has only unique values.
Beating a dead horse a bit, but with smaller sets where recursion limit and performance is not a problem, the general combination generation can be done recursively with "recurse combinations containing the first element in given array" plus "recurse combinations not containing the first element". It gives quite compact implementation as a generator:
// Generator yielding k-item combinations of array a
function* choose(a, k) {
if(a.length == k) yield a;
else if(k == 0) yield [];
else {
for(let rest of choose(a.slice(1), k-1)) yield [a[0], ...rest];
for(let rest of choose(a.slice(1), k)) yield rest;
}
}
And even slightly shorter (and twice faster, 1 M calls of 7 choose 5 took 3.9 seconds with my MacBook) with function returning and array of combinations:
// Return an array of combinations
function comb(a, k) {
if(a.length === k) return [a];
else if(k === 0) return [[]];
else return [...comb(a.slice(1), k-1).map(c => [a[0], ...c]),
...comb(a.slice(1), k)];
}