Related
I have been trying to accomplish this since yesterday, though no luck yet. I have found solutions where there always is a slight difference in what I want to accomplish.
I am trying to get all possible combinations, slightly like this: combination_k, but I also want the same items to pair up with itself, so given the following:
input [1, 4, 5] and 2 (number of combinations) should return:
[1, 1], [1, 4], [1, 5], [4, 4], [4, 5], [5, 5]
input [1, 4, 5] and 3 should return:
[1, 1, 1], [1, 1, 4], [1, 1, 5], [1, 4, 4], [1, 4, 5], [4, 4, 4], [4, 4, 5], [5, 5, 5], [5, 5, 4], [5, 5, 1] (The order is not important).
I have been adjusting combination_k, it got me far enough that it worked with 2 but it didn't work when I provided 3 as a parameter.
const combinations = getAllCombinations([1, 4, 5], 2);
// combinations = [1, 1], [1, 4], [1, 5], [4, 4], [4, 5], [5, 5]
Any tips are welcome!
The problem is commonly referred to as k-combinations with repetitions.
Here's a solution that relies on recursion to get the desired result:
const combinations = (array, r) => {
const result = [];
const fn = (array, selected, c, r, start, end) => {
if (c == r) {
result.push([...selected]);
return;
}
for (let i = start; i <= end; i++) {
selected[c] = array[i];
fn(array, selected, c + 1, r, i, end);
}
}
fn(array, [], 0, r, 0, array.length - 1);
return result;
}
console.log(combinations([1, 4, 5], 3));
A modified version of the code you provided:
function getAllCombinations(arr, n) {
if (n <= 0) return [];
if (n === 1) return [...arr];
return arr.reduce((acc, cur, i) => {
const head = arr.slice(i, i + 1);
const combinations = getAllCombinations(arr.slice(i), n - 1)
.map(x => head.concat(x));
return [...acc, ...combinations];
}, []);
}
console.log(getAllCombinations([1, 4, 5], 2).join('|'));
console.log(getAllCombinations([1, 4, 5], 3).join('|'));
For example, if your input array is [10, 5, 20], the output should be [2, 3, 1], since 10 is the second largest number, 5 is the third largest, and 20 is the largest.
This is my function:
function rankings(arr){
const result=[];
let newArr=arr.sort((a,b)=>b-a);
for (let i=0;i<arr.length;i++){
for (let j=0;j<newArr.length;j++){
arr[i]===newArr[j]? result.push(j+1): console.log('');
}
}
return(result);
}
Using my function with this array [10,5,20] as my input, my output is [1,2,3] while
rankings([10, 5, 20]); // [2, 3, 1] expected output
rankings([6, 8, 1, 12, 4, 3, 9]); // [4, 3, 7, 1, 5, 6, 2] expected output
You can also try to sort the array, and then match the indexes of the original array.
Edited to cater the case of repeated numbers
var originalArray = [10, 5, 20, 10, 20]
, setArray = [...new Set(originalArray)]
, sortedArray = [...setArray].sort((a, b) => a - b)
, dataIndex = originalArray.map(d => sortedArray.indexOf(d) + 1)
console.log(dataIndex)
You could sort the values and filter the items for avoiding same values with different ranks.
function rankings(array) {
const sorted = [...array]
.sort((a, b) => b - a)
.filter((b, i, { [i - 1]: a }) => a !== b);
return array.map(v => sorted.indexOf(v) + 1);
}
console.log(rankings([10, 5, 20])); // [2, 3, 1]
console.log(rankings([6, 8, 1, 12, 4, 3, 9])); // [4, 3, 7, 1, 5, 6, 2]
console.log(rankings([10, 5, 20, 10, 20]));
I'm currently building a tic tac toe in vanilla javascript. However the game is 'sort of' done but I'm trying to add levels of difficulty. So basically the thing I want to do is , on every player move , to get the the closest possible winning combination based on his moves and place computer's mark into the missing winning's combinations place.
Let's say I have multidimensional array with the winning combinations
winningCombinations: [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 4, 8],
[0, 3, 6],
[1, 4, 7],
[2, 4, 6],
[2, 5, 8]
]
And the player X starts his moves. So his first move is 0, so saving player's current combination in array. So in first move the current comb is
currentPlayerCombintation: [0]
so I want to return [0,1,2], [0,4,8] and [0,3,6] from the winning combination's array.
However the player makes his second move , so he target's 4 so
currentPlayerCombination: [0,4]
and now I want to return the closest possible winning combination which is [0,4,8].
I've tried a lot of things including every() , some() , filter() but could not achieve the thing I want.
I've tried sort of
for(let i = 0; i < this.currentPlayerCombination.length ; i++) {
this.winningCombinations.some((arr) => {
if(arr.includes(this.currentPlayerCombination[i])) {
console.log(arr);
}
});
}
But this didnt work as expected :(
You could take a Set and map the count of the matching items, get the max count and filter the array.
function getWinningPositions(pos) {
var posS = new Set(pos),
temp = winningCombinations.map(a => [a, a.reduce((c, v) => c + posS.has(v), 0)]),
max = Math.max(...temp.map(({ 1: c }) => c))
return temp
.filter(({ 1: c }) => c === max)
.map(([a]) => a);
}
var winningCombinations = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 4, 8], [0, 3, 6], [1, 4, 7], [2, 4, 6], [2, 5, 8]];
console.log(getWinningPositions([0]).map(a => a.join(' ')));
console.log(getWinningPositions([0, 4]).map(a => a.join(' ')));
console.log(getWinningPositions([0, 4, 5]).map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
First map the winningCombinations to an array of arrays whose numbers are only the numbers that have not been picked yet. Then, find the lowest length of those arrays, and you can identify the original winningCombinations which are closest to the currentPlayerCombination:
const winningCombinations = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 4, 8],
[0, 3, 6],
[1, 4, 7],
[2, 4, 6],
[2, 5, 8]
];
const currentPlayerCombination = [0, 4];
// eg: get [[1, 2], [3, 5,], [6, 7, 8], [8], ...]
const winningCombsWithoutCurrent = winningCombinations.map(arr => (
arr.filter(num => !currentPlayerCombination.includes(num))
));
// eg: here, lowestLength should be 1, because [8] has a length of 1
const lowestLength = winningCombsWithoutCurrent.reduce((a, { length }) => Math.min(a, length), 3);
const combosWithLowestLength = winningCombsWithoutCurrent
.reduce((a, { length }, i) => {
if (length === lowestLength) {
a.push(winningCombinations[i]);
}
return a;
}, []);
console.log(combosWithLowestLength);
Now I am working on a exercise in freecodecamp. Currently I got an logical error but do not why the failure happens.
In the code,I have to build in a function, which chop the input array based on the parameter. The testing result should be as follows:
chunkArrayInGroups(["a", "b", "c", "d"], 2) should return [["a", "b"], ["c", "d"]].
chunkArrayInGroups([0, 1, 2, 3, 4, 5], 3) should return [[0, 1, 2], [3, 4, 5]].
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 4) should return [[0, 1, 2, 3], [4, 5, 6, 7], [8]].
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2) should return [[0, 1], [2, 3], [4, 5], [6, 7], [8]].
And my code are as follows:
function chunkArrayInGroups(arr, size) {
var array = [];
for (var x = 0; x < arr.length ; x+=size){
var spliceArr = arr.splice(0,size);
array.push(spliceArr);
}
array.push(arr);
return array;
}
chunkArrayInGroups(["a", "b", "c", "d","e"], 2);
For most of the conditions, the code works. But for the last condition i.e
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2) should return [[0, 1], [2, 3], [4, 5], [6, 7], [8]].
in this case I cannot get the correct answer. I tested in console log, and turn out the output is like
[[0, 1], [2, 3], [4, 5], [6, 7, 8]].
I know that it is not a difficult question and there are lots of better way to approach it, but can I know what is the logic fallancy in this code?
Many thanks!
Instead of splice use slice. This will also guarantees that the original array is not modified.
Like this (working demo):
function chunkArrayInGroups(arr, size) {
var array = [];
for (var x = 0; x < arr.length; x += size) {
// take elements from current index (`x`) to `x` + `size`
// (do not remove them from the original array, so the original size is not modified either)
var sliceArr = arr.slice(x, x + size);
array.push(sliceArr);
}
return array;
}
console.log(chunkArrayInGroups(["a", "b", "c", "d"], 2)); //should return [["a", "b"], ["c", "d"]].
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5], 3)); // should return [[0, 1, 2], [3, 4, 5]].
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 4)); // should return [[0, 1, 2, 3], [4, 5, 6, 7], [8]].
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2)); // should return [[0, 1], [2, 3], [4, 5], [6, 7], [8]]
It might help to add a console.log(arr) to your loop to see how the array changes over time.
You would see that it looks like this:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[2, 3, 4, 5, 6, 7, 8]
[4, 5, 6, 7, 8]
Then, take into account your final splice and add which occurs outside of the loop:
[6, 7, 8]
Since your loop increments by size, it will exit once it has gathered all subarrays of exactly size.
Instead, I would recommend continuing until your input is empty:
function chunkArrayInGroups(arr, size) {
var array = [];
while(arr.length > 0){
var spliceArr = arr.splice(0,size);
array.push(spliceArr);
}
return array;
}
You will want to step using the size to save on the number of loops through the array. We are also saving the length so it's not fetched each time as it saves operations. Also you will notice that I'm not using var as you shouldn't be using it. Please use let for normal variables and const for variables you are not going to reassign.
function chunkArrayInGroups(arr, size) {
let array = [];
let arrayLength = arr.length;
for (let i = 0; i < arrayLength; i+=size) {
array.push(arr.slice(i, i+size));
}
return array
}
console.log(chunkArrayInGroups(["a", "b", "c", "d"], 2), [["a", "b"], ["c", "d"]])
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5], 3), [[0, 1, 2], [3, 4, 5]])
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 4), [[0, 1, 2, 3], [4, 5, 6, 7], [8]])
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2), [[0, 1], [2, 3], [4, 5], [6, 7], [8]])
The issue here is that you are reducing the array length throughout your iteration. I.e. your array gets smaller within each iteration while your x continously increases. That means that before your last iteration your x will be at 6 and the array length will be 3, hence x < arr.length evaluates to false and your last iteration does not happen. The most simplistic solution that I can think of is to store the original array length into a variable I named stop and remove the unneccessary final array push outside the loop.
function chunkArrayInGroups(arr, size) {
var array = [];
var stop = arr.length;
for (var x = 0; x < stop; x+=size){
var spliceArr = arr.splice(0,size);
array.push(spliceArr);
}
return array;
}
console.log(chunkArrayInGroups([1,2,3,4,5,6,7], 2))
splice method changes the length of array on every iteration. That's why your loop exits before you expect. You can read more about splice here.
Unlike splice, slice will not remove items from the array that's why lealceldeiro answer will work as expected.
Kevin Bruccoleri answer looks cleaner and shorter but if you have an app where you store an array in to a variable and then pass it to the function, that variable will be empty after the execution of the function, which can lead to bugs in your app. That's why arrays are basically object, but that's science fiction of javascript.
function chunkArrayInGroups(arr, size) {
var array = [];
while (arr.length) {
array.push(arr.splice(0, size))
}
return array
}
var nums = [0, 1, 2, 3, 4, 5, 6, 7, 8]
console.log('now it full', nums);
console.log(chunkArrayInGroups(nums, 2));
console.log('now it empty', nums);
Used slice to copy original array two
map() and splice() to insert array from n index
const frankenSplice = (arr1, arr2, n) => {
let arr = arr2.slice();
arr1.map(e => {
arr.splice(n, 0, e);
n++;
})
return arr;
}
I am trying to solve this freecodecamp algorithm question where I had to collect the difference of two or more arrays. I used map to get the difference of array but the problem is I only get two elements;
function sym(args) {
args = [].slice.call(arguments);
var newArr = args.map(function(el, index, arr){
console.log(arr.indexOf(arr[index]));
if(arr.indexOf(arr[index] === -1 )){
// console.log(arr[index]);
return args.push(arr[index]);
}
});
return newArr; // my newArr returns [3, 4] instead of [3,4,5]
}
console.log(sym([1, 2, 3], [5, 2, 1, 4]));
//sym([1, 2, 3], [5, 2, 1, 4]) should return [3, 4, 5]
//sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]) should return [1, 2, 4, 5, 6, 7, 8, 9]
I think we could do also this way since we want them to be ordered at the end.
For more detail about the original problem please consult this link: FreecodeCamp Link: Symmetric Difference
const sym = (...args) => {
// Merge all the different arrays and remove duplicate elements it means elements that are present both on two related arrays
let tab = args.reduce((a, b) => [
...a.filter(i => !b.includes(i)),
...b.filter(j => !a.includes(j))
], []);
// Then remove the rest of duplicated values and sort the obtained array
return Array.from(new Set(tab)).sort((a, b) => a - b);
}
console.log(sym([1, 2, 3, 3], [5, 2, 1, 4])); // [3, 4, 5]
console.log(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5])); // [1, 4, 5]
console.log(sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1])); // [1, 2, 4, 5, 6, 7, 8, 9]
The Set data structure is used here to remove duplicated values thanks to its characteristics.
Well your function is a little more complex than only selecting the unique values, cause you want to filter them out... and also accept multiple arrays. This should work.
var sym = (...arrays)=>{
//Concat Items
const allItems = arrays.reduce((a,c)=>a.concat(c), []);
// Identify repeated items
const repeatedItems = allItems.filter((v,i,a)=>a.indexOf(v) !== i);
// Filter repeated items out
const diff = allItems.filter(item=>repeatedItems.indexOf(item) < 0);
console.log(diff);
};
sym([1, 2, 3], [5, 2, 1, 4]); // [3,5,4]
I don't think your approach will work; you're supposed to create an array with elementos from both arrays, so a single .map won't do the job. Filtering through both arrays should work, although it will probably leave enough room for optimization.
my newArr returns [3, 4] instead of [3,4,5]
You are using map which will only return one value per iteration (which is why you are getting only 2 values) and in your case you are checking if the index is found or not (not the item)
You need to concatenate all the arrays and then remove those which are repeated
Concatenate
var newArr = args.reduce( ( a, c ) => a.concat( c ) , []);
Create a map by number of occurrences
var map = newArr.reduce( (a,c) => ( a[c] = (a[c] || 0) + 1, a ) , {});
Iterate and filter through those keys whose value is 1
var output = Object.keys( map ).filter( s => map[s] === 1 ).map( Number );
Demo
function sym(args)
{
args = [].slice.call(arguments);
var newArr = args.reduce( ( a, c ) => a.concat( c ) , []);
var map = newArr.reduce( (a,c) => ( a[c] = (a[c] || 0) + 1, a ) , {});
return Object.keys( map ).filter( s => map[s] === 1 ).map( Number );
}
console.log(sym([1, 2, 3], [5, 2, 1, 4]));
You could take an Object for counting the items and return only the items which have a count.
function sym(array) {
return array.reduce((a, b) => {
var count = {};
a.forEach(v => count[v] = (count[v] || 0) + 1);
b.forEach(v => count[v] = (count[v] || 0) - 1);
return Object.keys(count).map(Number).filter(k => count[k]);
});
}
console.log(sym([[3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]]));