All possible unique combinations of a set in JavaScript - javascript

I'm building an app to test different icons. Admins upload a number of icons and input how many icons must be shown at the same time. The app then displays all possible sets of icons in sequence until all combination of icons have been shown.
Now, I need a function to generate all unique icons combinations based on two number:
the number of total icons (i)
the number of icons in each set (s)
If i = 6 and s = 3, I want the output to look as follows:
[
[1, 2, 3],
[1, 2, 4],
[1, 2, 5],
[1, 2, 6],
[1, 3, 4],
[1, 3, 5],
[1, 3, 6],
[1, 4, 5],
[1, 4, 6],
[1, 5, 6],
[2, 3, 4],
[2, 3, 5],
[2, 3, 6],
[2, 4, 5],
[2, 4, 6],
[2, 5, 6],
[3, 4, 5],
[3, 4, 6],
[3, 5, 6],
[4, 5, 6],
]
Requirements:
All sets have to be unique
A number can only occur one time in a set
I have been trying to code a recursive function, but I havent anything really anything to show. I can't get my head around it :(

Based off the idea given as an answer to this question:
Computing all n-sized permutations without repetitions and without "classic" ordering
Then use C++ std::next_permutation like algorithms which work as
follows:
Go from left and find rightmost one preceeded by zero. Put one in
place of zero and sort the rest of array.
Disclaimer: My javascript is very, very rusty so I'm sure there is a much more elegant way of implementing this.
function combine(n, k) {
var result = [];
// initialize array of values
var values = [];
for (var i = 1; i <= n; i++) {
values[i - 1] = i;
}
// initialize permutations
var perm = [];
for (var i = 0; i < n; i++) {
if (i < k) {
perm[i] = 1;
} else {
perm[i] = 0;
}
}
perm.sort();
whileloop:
while (true) {
// save subresult
var subresult = [];
for (var i = 0; i < n; i++) {
if (perm[i] == 1) {
subresult.push(values[i]);
}
}
result.push(subresult);
// get next permutation
for (var i = n - 1; i > 0; i--) {
if (perm[i - 1] == 1) {
continue;
}
if (perm[i] == 1) {
perm[i - 1] = 1;
perm[i] = 0;
perm = perm.slice(0, i).concat(perm.slice(i).sort())
continue whileloop;
}
}
// no additional permutations exist
break whileloop;
}
return result;
}

Combining n elements in to number of sets with k elements each without repetitions, how to do it.
The algorithm is relatively simple, main idea: we consequently feed first set of k elements and then try to increment each element from the end of set to populate another k-set and so on.
When we can't do that we leave the process (all possible sets are ready).
function combine(n,k) {
var result = Array();
var a = Array();
// make initial (first) k-set
for (var i=1; i<=k; i++) {
a[i-1] = i;
}
j = k-1;
while (j >= 1) {
// submit current results
result.push(a.slice());
if (a[k-1] == n) {
j = j - 1;
} else {
j = k-1;
}
if (j >= 1) {
// make next k-set based on previous one
for (var i=k; i>=j; i--) {
a[i-1] = a[j-1] + i - j + 1;
}
}
}
return result;
}
Note: JavaScript arrays have start index 0 so in code we have -1 correction for array indices (cause set of possible values from 1 to n)

Related

Algorithm problem with array subsequences [duplicate]

I am trying to write a function that will take an array and n as parameters,
it will return all subsets of that array with n elements, have tried a couple things, couldn't yet succeed.
thanks to whoever put it here, this functions is way too complicated and doesn't do the job, basically what I tried to do here is to pick out one element from a 4 element array to create its 3 element subsets. It doesn't even take N as parameter. it returns all 3 element subsets but also identical ones, so I have to filter them out as well, in any case I will keep trying.
function findSubsets(array) {
var answers = [];
var firstArray = array;
for (i = 0; i < array.length; i++) {
array = firstArray;
for (var k = 0; k < array.length; k++) {
if (k != i) {
var subset = array.splice(k, 1);
answers.push(array); array.splice(k, 0, subset[0]);
}
}
}
}
That not as complicated as it seems. This one is optimized because it doesn't creates useless temporary arrays during the process.
function findSubsets(array, n) {
var answers = [];
for(var i = 0 ; i < array.length ; i += n) {
answers.push(array.slice(i, i + n));
}
return answers;
}
findSubsets([1, 2, 3, 4, 5, 6, 7, 8, 9], 2) // --> [[1, 2], [3, 4], [5, 6], [7, 8], [9]]
findSubsets([1, 2, 3, 4, 5, 6, 7, 8, 9], 3) // --> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
You can try this solution
var subsetArray = (function() {
return {
getResult: getResult
}
function getResult(array, n) {
function isBigEnough(value) {
return value.length === n;
}
var ps = [
[]
];
for (var i = 0; i < array.length; i++) {
for (var j = 0, len = ps.length; j < len; j++) {
ps.push(ps[j].concat(array[i]));
}
}
return ps.filter(isBigEnough);
}
})();
var arr = [1, 2, 3, 4,5,6,7,8,9];
console.log(subsetArray.getResult(arr,2));

Bubble Sort with for loop doesn't work as expected

I just got started with writing sorting algorithms.
Currently I am learning the Bubble Sort algorithm, I found the following online and it's working fine:
const arr = [3, 2, 6, 9, 3, 5];
const bubbleSort = array => {
do {
var isSorted = true;
for (var i = 0; i < array.length; i++) {
if (array[i] > array[i + 1]) {
var temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
isSorted = false;
}
}
} while(!isSorted)
return array
};
Output:
[ 2, 3, 3, 5, 6, 9 ]
However, when I try to write it using an if statement instead of a while loop like the following, it doesn't work as expected:
const arr = [3, 2, 6, 9, 3, 5];
const bubbleSort = (array) => {
let isSorted = false;
if(!isSorted) {
isSorted = true;
for(var i = 0; i < array.length; i++) {
if (array[i] > array[i + 1]) {
var temp = array[i + 1];
array[i + 1] = array[i];
array[i] = temp;
isSorted = false;
}
}
}
return array;
}
Output:
[ 2, 3, 6, 3, 5, 9 ]
What am I doing wrong here?
We need the while loop for bubble sort.
If we remove while then we will 'bubble' only once through the whole array. For example if the
[3, 2, 6, 9, 3, 5];
here 3 (first element) is larger than 2 (second element) so we swap them and now we have
[2, 3, 6, 9, 3, 5]
When we continue with the for loop we approach 3 (6th element) that is smaller so we swap it with 9 (5th element). AND continue forward.
[2, 3, 6, 3, 9, 5]
from here we will only go up but we can analyse the situation. We can see that 3 (4th element) is smaller than 6(third element) but the for loop is way ahead so we will not be in a situation where we swap it with a larger element.
So we have to start "bubbling" again from the beginning and we need to do it until everything is sorted. This will happen when there are nothing to swap, because we set isSorted=false when ever we swap. After array is sorted we will do a last pass where we will check every adjacent pair and if they are all sorted the swap will not occur and isSorted will be true
TLDR; we need while because 'bubbling' might need several passes through the array.
while loop is an easy way to implement Bubble Sort, otherwise you will need O(n²) algorithm with nested for loops instead, if else statements will not work:
const arr = [3, 2, 6, 9, 3, 5];
const bubbleSort = array => {
var length = array.length;
//Number of passes
for (var i = 0; i < length; i++) {
//Notice that j < (length - i)
for (var j = 0; j < (length - i - 1); j++) {
//Compare the adjacent positions
if(array[j] > array[j+1]) {
//Swap the numbers
var tmp = array[j]; //Temporary variable to hold the current number
array[j] = array[j+1]; //Replace current number with adjacent number
array[j+1] = tmp; //Replace adjacent number with current number
}
}
}
return array
}
bubbleSort(arr)
// expected output: [2, 3, 3, 5, 6, 9]
You can also see Bubble Sort pseudocode here: Bubble Sort Algorithm

How to find the greatest number of times each element occurs in a nested array?

How to find the greatest number of times each element occurs in a nested array?
I am looking to find the greatest number of times each element occurs in any of the subarrays. I am NOT looking for the element that occurs the most times in the entire nested array.
Let's say my nested array is [[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]].
The number 2 appears two times in one of the subarrays. The number 3 appears two times in one of the subarrays. The number 5 appears one time in one of the subarrays.
The end result I am looking for is [2, 2, 3, 3, 5].
What is the best way to do this? Below is my approach, which is not very good.
function makeNewArray(arr) {
// add the # to the numbers that appear once and add the ## to the numbers that appear twice
for (var j = 0; j < arr.length; j++) {
for (var i = 0; i < arr[j].length; i++) {
if (arr[j][i] === arr[j][i+1]) {
arr[j][i] = arr[j][i] + '#';
arr[j][i+1] = arr[j][i+1] + '#';
} else {
arr[j][i] = arr[j][i] + '#';
}
}
}
// flatten the array
arr = arr.reduce(function(a, b) { return a.concat(b); });
// remove the duplicates from the array
arr = arr.filter(function(a, b) { return arr.indexOf(a) == b; });
// remove the ## and # from the array
for (var i = 0; i < arr.length; i++) {
arr[i] = parseInt(arr[i]);
}
return arr;
}
makeNewArray([[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]]);
Just based on your question and not the result you expect that I don't really get, here is a working solution that will find the highest number of occurrence.
var a = [
[2, 3, 5],
[3, 3, 5],
[2, 2, 3, 5]
];
var o = {};
var max = 0;
var highest = null;
for (var i = 0; i < a.length; i++) {
for (var j = 0; j < a[i].length; j++) {
if (!o.hasOwnProperty(a[i][j])) {
o[a[i][j]] = 1;
} else {
o[a[i][j]]++;
}
if (o[a[i][j]] > max) {
max = o[a[i][j]];
highest = a[i][j];
}
}
}
//this is the number with the highest occurence
console.log(highest);
This ES6 solution iterates the sub arrays, and creates a map of the values, then in it moves the highest values to a map of the entire array. Afterwards, we map the Map entries (no pan intended) to new arrays that are filled with the numbers according to their highest count, and flatten the result.
var data = [[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]];
var result = [].concat(... // flatten the end result
[... // convert the Map to entries array
data.reduce((r, s) => { // reduce the array into a map of counts
s.reduce((map, num) => map.set(num, (map.get(num) || 0) + 1), new Map) // get a Map of the current sub array counts
.forEach((v, k) => r.set(k, Math.max(r.get(k) || 0, v))); // update the global Map if the sub array count of a number is higher
return r;
}, new Map)]
.map((s) => Array.from({ length: s[1] }, () => s[0]))); // map the entries into new sub arrays
console.log(result);

javascript optimization for pair finding algorithm

I am working on a javascript function which takes an array of integers and a target as arguments. The task is to find the first pair of integers in the array whose sum is equal to the target. I have tried this several different ways, but I keep getting a timeout error for larger input arrays. Can someone please give me some pointers on how to better optimize this code? Thanks!
var sum_pairs = function(ints, s){
var r = [];
var a = true;
var l = ints.length;
for(var j = 0; j < l; j++){
if(a){
for(var i = 0; i < j; i++){
if(ints[j] + ints[i] == s){
r[0] = ints[i];
r[1] = ints[j];
a = false;
break;
}
}
}
else{
console.log('breaking');
break;
}
}
return r[0] == null ? null : r;
}
You could use some speeding mechanisms, like
single loop,
hash table for visited values
variable a for element array[i]
very short variable names (just kidding)
Long list needs 153 ms.
var sum_pairs = function (array, s) {
var a, i,
hash = Object.create(null);
for (i = 0; i < array.length; i++) {
a = array[i];
if (hash[s - a]) {
return [s - a, a];
}
if (!hash[a]) {
hash[a] = true;
}
}
};
console.log(sum_pairs([11, 3, 7, 5], 10)); // [3, 7]
console.log(sum_pairs([4, 3, 2, 3, 4], 6)); // [4, 2]
console.log(sum_pairs([0, 0, -2, 3], 2)); // undefined
console.log(sum_pairs([10, 5, 2, 3, 7, 5], 10)); // [3, 7]
console.log(sum_pairs([1, 2, 3, 4, 1, 0], 2)); // [1, 1]
console.log(sum_pairs([1, -2, 3, 0, -6, 1], -6)); // [0, -6]
console.log(sum_pairs([0, 2, 0], 0)); // [0, 0]
console.log(sum_pairs([5, 9, 13, -3], 10)); // [13, -3]
.as-console-wrapper { max-height: 100% !important; top: 0; }
For each number that we encounter while iterating the array, we add that number's expected partner target - number into a Set. As soon as we encounter a number that is already in our set, we know that its partner has already been encountered and return this pair as the solution:
// Return the first two values of 'numbers' summing up to 'target':
function sum_pairs(numbers, target) {
let paired = new Set();
for (let number of numbers) {
if (paired.has(number)) return [target - number, number];
paired.add(target - number);
}
}
// Examples:
console.log(...sum_pairs([9, 3, 7, 5, 1], 10)); // [3, 7]
console.log(...sum_pairs([4, 3, 2, 3, 4], 6)); // [4, 2]
console.log(...sum_pairs([9, 3, 6, 4, 1], 10)); // [6, 4]
This implementation has a linear runtime complexity and is therefore faster for long input arrays, but it comes with an additional memory cost.
If you are going for raw speed, replace the for-of loop with a traditional for-loop and the let variable binding with a var declaration.

Finding unused combinations

What is a fast way to find combinations that aren't present in an array yet?
E.g, I have list of points: [1, 2, 4, 9]
And I have a list of connections [[1,2], [1,4], [1,9], [2,4], [4,9]]
So the missing connection in this list is [2,9]. As there is one requirement: every integer must be connected to a bigger integer.
var points = [1, 2, 4, 9];
var connections = [[1,2], [1,4], [1,9], [2,4], [4,9]];
var missing = [];
for(i = 0; i < points.length; i++){
for(j = i + 1; j < points.length; j++){
var found = false;
for(var a = 0; a < connections.length; a++){
if(connections[a][0] == points[i] && connections[a][1] == points[j]){
found = true;
break;
}
}
if(!found) missing.push([points[i], points[j]]);
}
}
console.log(missing);
The above code works, but the amount of for loops makes me think it is reasonably slow. Is there any faster way to do this? View jsfiddle
By sorting the array, you can do it with 2 nests. Sorting takes O(n log n), and the loops are basically O(n ^ 2).
var points = [1, 2, 4, 9];
var connections = [
[1, 2],
[1, 4],
[1, 9],
[2, 4],
[4, 9]
];
connections.sort();
var missing = [];
var currentIndex = 0;
for (var i = 0; i < points.length; i++) {
for (var j = i + 1; j < points.length; j++) {
if (connections[currentIndex][0] == points[i] && connections[currentIndex][1] == points[j]) {
currentIndex++;
} else {
missing.push([points[i], points[j]]);
}
}
}
console.log(missing);
You can use .reduce method in order to generate all the combination of two elements.Then the only thing that will remain is to get the difference from two arrays.
For this, you can use filter method which accepts a callback method.
var points = [1, 2, 4, 9];
points=points.sort();
var connections = [[1,2], [1,4], [1,9], [2,4], [4,9]];
var combinations = points.reduce(function(arr,elem,i){
for(j=i+1;j<points.length;j++)
arr.push([elem,points[j]]);
return arr;
},[]);
var diff=combinations.filter(function(elem,i){
return connections.find(a=>a[0]==elem[0] && a[1]==elem[1])==undefined;
});
console.log(diff);
You could iterate only the outer loop until length - 2 and use a hash table for inserted connections. The sort order of connections does not matter.
var points = [1, 2, 4, 9],
connections = [[1, 2], [1, 4], [1, 9], [2, 4], [4, 9]],
missing = [],
i, j,
pair,
connected = Object.create(null);
connections.forEach(function (a) {
connected[a.join()] = true;
});
for (i = 0; i < points.length - 1; i++) {
for (j = i + 1; j < points.length; j++) {
pair = [points[i], points[j]];
connected[pair.join()] || missing.push(pair);
}
}
console.log(missing);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories