Related
How to completely remove repeated numbers from an array?
For example, if:
const array = [1, 1, 2, 3, 1, 2, 5]
The output should be:
[3, 5]
You could take an object for keeping track of seen items, by taking an array with the value or set the arrays value to zero.
Finally flat the result set to remove empty arrays.
const
array = [1, 1, 2, 3, 1, 2, 5],
result = array
.reduce((o => (r, v) => {
if (v in o) o[v].length = 0;
else r.push(o[v] = [v]);
return r;
})({}), [])
.flat();
console.log(result);
Roko and Ke1vans had answered in functional approaches. Both of them are correct. However, I'd show an answer in imperative approach, which may seems easier for new comer.
Similar to their flow. First, we count the occurrence of each number. Then we select the numbers that has occurred once (hence being non-repeated) into the output array.
let array = [1,1,2,3,1,2,5]
let counts = {}
let output = []
// loop each elements in the array as `item`
for(let item of array) {
// If the item is not set in the counts, `counts[item]` will be `undefined`.
// Using `|| 0` means use zero as fallback value if the items is unseen.
let count = counts[item] || 0
counts[item] = count + 1
}
// loop each keys in the object (key-value pairs) as `item`
for(let item in counts) {
let count = counts[item]
if(count == 1) {
// `+item` converts the key from string into number
output.push(+item)
}
}
console.log(output) // will print out `[ 3, 5 ]`
You can iterate and create a map of values. later iterate and filter.
const data = [1, 1, 2, 3, 1, 2, 5];
const findUniques = (data = []) => {
const map = data.reduce((m, num) => {
m[num] = (m[num] || 0) + 1;
return m;
}, {});
return data.filter((num) => map[num] === 1);
};
console.log(findUniques(data));
You can also do the same using 2 set, or 2 array.
const data = [1, 1, 2, 3, 1, 2, 5];
const findUniques2 = (data = []) => {
let unique = new Set();
let seen = new Set();
for (let num of data) {
if (seen.has(num)) unique.delete(num);
else unique.add(num);
seen.add(num);
}
return Array.from(unique);
};
console.log(findUniques2(data));
Use an Object where the key is the number, and the value is the number of occurrences. Than reduce it back to the desired array of values:
const arr = [1,1,2,3,1,2,5];
const res = Object.entries(arr.reduce((ob, v) => {
if (!(v in ob)) ob[v] = 0;
ob[v] += 1; // Count occurrences
return ob;
}, {})).reduce((arr, [k, v]) => { // Reduce back to Array
if (v === 1) arr.push(+k); // Only keys with 1 occurrence
return arr;
}, []);
console.log(res); // [3, 5]
You can use Array.filter() (Array filter article) for this for Example :
const a = [ 1 , 1 , 2 , 3 , 2 , 4 , 5 , 7];
function filter(value , index , array){
// looping through all the objects in the array
for(let i=0; i<array.length; i++) {
if(i != index && array[i] == value) return false; // return 'false' if the value has a duplicate other than itself
}
// return 'TRUE' if value hasn't been duplicated
return true;
}
const b = a.filter(filter); // [3, 4, 5, 7]
And the short version if this function's going to be used only once:
const a = [ 1 , 1 , 2 , 3 , 2 , 4 , 5 , 7];
const b = a.filter((value , index , array) => {
for(let i=0; i<array.length; i++) if(i != index && array[i] == value) return false;
return true;
});
// [3, 4, 5, 7]
You could find duplicates and then operate a difference.
let a = [1,1,2,3,1,2,5];
const findDuplicates = (nums) => {
nums.sort(); // alters original array
let ans = []
for(let i = 0; i< nums.length; i++){
if(nums[i] === nums[i+1]){
if(ans[ans.length -1] !== nums[i]){
ans.push(nums[i])
}
}
}
return ans;
}
duplicates = new Set(findDuplicates(a))
let difference = new Set([...a].filter(x => !duplicates.has(x)))
console.log(Array.from(difference))
output : [ 3, 5 ]
Note : I grab the findDuplicates function from this link
const data = [1, 1, 2, 3, 1, 2, 5];
const s = new Set();
const res = data.filter((a, i) => {
if (data.lastIndexOf(a) > i || s.has(a)) {
s.add(a);
return false;
}
return true;
});
console.log(res); //=> [3, 5]
Given an array and an index, I'd like to return n values surrounding the value at index.
For instance:
Array: [0,1,2,3,4,5,6]
index: 1
values: 2
Result:
[6,0,1,2,3]
slice comes in handy but I can't get the wrapping requirement working. Is the simplest solution to concat the array and work from that? ([...array, ...array])
You could take the double array and an offset with adjustment for slicing.
const
getValues = (array, index, values) => {
let offset = index - values;
if (offset < 0) offset += array.length;
return [...array, ...array].slice(offset, offset + 2 * values + 1);
},
array = [0, 1, 2, 3, 4, 5, 6],
result = getValues(array, 1, 2);
console.log(...result);
I'm not sure this adds much to the answers here already unless it might need to wrap over a size greater than the array length:
const circle_number_for_size = (circle_size) => (number) => {
const rem = number % circle_size;
return rem + (rem < 0 ? circle_size : 0);
};
const circle_slice = (array, start, end) => {
const circle_number = circle_number_for_size(array.length);
let new_array = [];
for(let i = start; i <= end; i++)
new_array.push(array[circle_number(i)]);
return new_array;
};
const neighbours = (array, centre_index, offset) =>
circle_slice(array, centre_index - offset, centre_index + offset);
console.log( ...neighbours([0,1,2,3,4,5,6], 1, 2) );
console.log( ...neighbours([0,1,2,3,4,5,6,7,8,9], 0, 21) );
I want to know how can I add 3 different number to the 3n, 3n+1 and 3n+2 indices. I mean for example I have following array :
var arr = [1,1,1,2,2,2,3,3,3]
and then I want add the (3n)th to 5 and then I want add (3n+1)th of an array to 2 and (3n+2) to 3,
I mean the final array I want to be like following result array:
var result = [6,3,4,7,4,5,8,5,6]
and I try to do it as following code:
// arr = [1,1,1,2,2,2,3,3,3]
let res = [];
for (let i = 0; i < arr.length; i++) {
res.push([arr[i*3] * 5,
arr[(i*3)+1] *2,
arr[(i*3)+2] *3])
}
This should do the trick:
var arr = [1,1,1,2,2,2,3,3,3],
add = [5,2,3], res=[];
// result = [6,3,4,7,4,5,8,5,6]
for (let i=0;i<arr.length;i+=add.length) add.forEach((v,j)=>res[i+j]=arr[i+j]+v);
console.log(JSON.stringify(res))
An alternative and even shorter solution (similar to #Robin's answer) would be:
var arr = [1,1,1,2,2,2,3,3,3],
add = [5,2,3], res=[];
res=arr.map((v,i)=>v+add[i%add.length]);
console.log(JSON.stringify(res))
( I noticed #Nina came up with a very similar answer ...)
You can simply use map, making use of the fact that its function argument takes the current index an optional second argument:
var arr = [1,1,1,2,2,2,3,3,3];
var result = arr.map((num, idx) => {
switch (idx % 3) {
case 0:
return num + 5;
case 1:
return num + 2;
case 2:
return num + 3;
}
});
console.log(result);
You could mapp the array directly by taking a closure over an index for the values array for adding.
var array = [1, 1, 1, 2, 2, 2, 3, 3, 3],
add = [5, 2, 3],
result = array.map((i => v => v + add[i++ % add.length])(0));
console.log(...result);
I need to get all possible subsets of an array.
Say I have this:
[1, 2, 3]
How do I get this?
[], [1], [2], [3], [1, 2], [2, 3], [1, 3], [1, 2, 3]
I am interested in all subsets. For subsets of specific length, refer to the following questions:
Finding subsets of size n: 1, 2
Finding subsets of size > 1: 1
Here is one more very elegant solution with no loops or recursion, only using the map and reduce array native functions.
const getAllSubsets =
theArray => theArray.reduce(
(subsets, value) => subsets.concat(
subsets.map(set => [value,...set])
),
[[]]
);
console.log(getAllSubsets([1,2,3]));
We can solve this problem for a subset of the input array, starting from offset. Then we recurse back to get a complete solution.
Using a generator function allows us to iterate through subsets with constant memory usage:
// Generate all array subsets:
function* subsets(array, offset = 0) {
while (offset < array.length) {
let first = array[offset++];
for (let subset of subsets(array, offset)) {
subset.push(first);
yield subset;
}
}
yield [];
}
// Example:
for (let subset of subsets([1, 2, 3])) {
console.log(subset);
}
Runtime complexity is proportional to the number of solutions (2ⁿ) times the average length per solution (n/2) = O(n2ⁿ).
Simple solution without recursion:
function getAllSubsets(array) {
const subsets = [[]];
for (const el of array) {
const last = subsets.length-1;
for (let i = 0; i <= last; i++) {
subsets.push( [...subsets[i], el] );
}
}
return subsets;
}
How does it work?
If we have some subsets generated from input numbers and we want to add one more number to our input array, it means that we can take all already existing subsets and generate new ones by appending the new number to each of the existing.
Here is an example for [1, 2, 3]
Start with an empty subset: []
Create new subsets by adding "1" to each existing subset. It will be:[] [1]
Create new subsets by adding "2" to each existing subset. It will be:[], [1] [2], [1, 2]
Create new subsets by adding "3" to each existing subset. It will be: [], [1], [2], [1, 2] [3], [1, 3], [2, 3], [1, 2, 3]
Another simple solution.
function getCombinations(array) {
function fork(i, t) {
if (i === array.length) {
result.push(t);
return;
}
fork(i + 1, t.concat([array[i]]));
fork(i + 1, t);
}
var result = [];
fork(0, []);
return result;
}
var data = [1, 2, 3],
result = getCombinations(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can easily generate the powerset from an array, using something like the following:
var arr = [1, 2, 3];
function generatePowerSet(array) {
var result = [];
result.push([]);
for (var i = 1; i < (1 << array.length); i++) {
var subset = [];
for (var j = 0; j < array.length; j++)
if (i & (1 << j))
subset.push(array[j]);
result.push(subset);
}
return result;
}
console.log(generatePowerSet(arr));
Throughout the main loop of the function, subsets are created and then pushed into the result array.
I set out to understand what is happening with the examples in this post. While the function generator example, bit-wise operator example, and the example use of the array map and reduce functions are very elegant and impressive, I found it tough to mentally visual what precisely was happening. I have 2 examples below that I believe are easy to visualize both a non-recursive and a recursive solution. I hope this helps others attempting to wrap their heads around the process of finding all subsets.
NON-RECURSIVE:
For each value of the array clone all existing subsets (including the empty set) and add the new value to each of the clones, pushing the clones back to the results.
const PowerSet = array => {
const result = [[]] // Starting with empty set
for (let value of array) { // For each value of the array
const length = result.length // Can't use result.length in loop since
// results length is increased in loop
for (let i = 0; i < length; i++){
let temp = result[i].slice(0) // Make a clone of the value at index i
temp.push(value) // Add current value to clone
result.push(temp) // Add clone back to results array
}
}
return result;
}
console.log(PowerSet([1,2,3]))
RECURSIVELY:
Build the powerset by recursively pushing a combination of the current index value concatenated with an ever increasing prefix array of values.
const powerSetRecursive = (arr, prefix=[], set=[[]]) => {
if(arr.length === 0) return// Base case, end recursion
for (let i = 0; i < arr.length; i++) {
set.push(prefix.concat(arr[i]))// If a prefix comes through, concatenate value
powerSetRecursive(arr.slice(i + 1), prefix.concat(arr[i]), set)
// Call function recursively removing values at or before i and adding
// value at i to prefix
}
return set
}
console.log(powerSetRecursive([1,2,3]))
function subSets(num){
/*
example given number : [1,3]
[]
1: copy push 1
[] [1]
3: copy push 3
[] [1] [3] [1,3]
*/
let result = [];
result.push([]);
for(let i=0; i < num.length;i++){
let currentNum = num[i];
let len = result.length;
for(let j=0; j < len; j++){
let cloneData = result[j].slice();
cloneData.push(currentNum);
result.push(cloneData)
}
}
return result;
}
let test = [1,3];
console.log(subSets(test))//[ [], [ 1 ], [ 3 ], [ 1, 3 ] ]
let subsets = (n) => {
let result = [];
result.push([]);
n.forEach(a => {
//array length
let length = result.length;
let i =0;
while(i < length){
let temp = result[i].slice(0);
temp.push(a);
result.push(temp);
i++;
}
})
return result;
}
Using flatMap and rest/spread, this can be fairly elegant:
const subsets = ([x, ...xs]) =>
x == undefined
? [[]]
: subsets (xs) .flatMap (ss => [ss, [x, ...ss]])
console .log (subsets ([1, 2, 3]))
.as-console-wrapper {max-height: 100% !important; top: 0}
This version does not return them in the requested order. Doing that seems slightly less elegant, and there's probably a better version:
const subset = (xs = []) => {
if (xs.length == 0) {return [[]]}
const ys = subset (xs .slice (0, -1))
const x = xs .slice (-1) [0]
return [... ys, ... ys .map (y => [... y, x])]
}
Or, the same algorithm in a different style,
const subsets = (
xs = [],
x = xs .slice (-1) [0],
ys = xs.length && subsets (xs .slice (0, -1))
) =>
xs .length == 0
? [[]]
: [... ys, ... ys .map (y => [... y, x])]
A shorter version of #koorchik's answer.
var getAllSubsets = (nums) => {
const subsets = [[]];
for (n of nums) {
subsets.map((el) => {
subsets.push([...el, n]);
});
}
return subsets;
};
console.log(getAllSubsets([1, 2, 3]));
// [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
For loop:
function powerSet(numbers) {
const subsets = [[]]
for (const number of numbers) {
subsets.forEach(subset => subsets.push([...subset, number]))
}
return subsets
}
Recursion:
function powerSet(numbers) {
const subsets = [[]]
if (numbers.length === 0) return subsets
for (let i = 0; i < numbers.length; i++) {
subsets.push(...powerSet(numbers.slice(i + 1)).map(subset => [numbers[i], ...subset]))
// Or step by step:
// const number = numbers[i]
// const otherNumbers = numbers.slice(i + 1)
// const otherNumbersSubsets = powerSet(otherNumbers)
// const otherNumbersSubsetsWithNumber = otherNumbersSubsets.map(subset => [number, ...subset])
// subsets.push(...otherNumbersSubsetsWithNumber)
}
return subsets
}
Using reduceRight:
const subsets = array =>
array.reduceRight(
(accumulator, a) => [...accumulator, ...accumulator.map(b => [a, ...b])],
[[]]
);
console.log(subsets([1, 2, 3])); // [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
This one is with recursion
var subsets = function(s){
if(s.length === 0) {
return [[]]
}
var h,t,ss_excl_h;
var ss_incl_h = [];
[h,...t] = s;
ss_excl_h = subsets(t)
for(ss of ss_excl_h) {
let hArr = [];
hArr.push(h);
let temp = hArr.concat(ss)
ss_incl_h.push(temp);
}
return ss_incl_h.concat(ss_excl_h)
}
console.log(subsets([1,2,3])) // returns distinct subsets
Update ES2020
With ES2020 BigInts have become available.
Bigints don’t have a fixed storage size in bits; their sizes adapt to the integers they represent.
- Dr. Axel Rauschmayer; JavaScript for impatient programmers - Chapter 18.2 BigInts
See source.
Using BitInts we can use a binary counter to calculate the power set and are no longer limited by the maximum integer size.
Using a generator we can additionally loop over a power set with constant memory requirement which is important if you want to generate a huge power set.
Here an example using you original array [1, 2, 3].
/**
* Generate power set from a given array
* #param {Array<any>} array array to create power set from
*/
function* powerSet(array){
// use BigInt to be no longer limited by maximum length of 53-bits
const size = 2n ** BigInt(array.length);
for (let i = 0; i < size; i++) {
const cur = [];
for(let j = 0; j < array.length; j++){
// check if i-th bit is set to 1
if((i & (1 << j)) > 0){
// push array value (represented by that 1-bit) to result
cur.push(array[j]);
}
}
// generate next result
yield cur;
}
}
// generate power set for [1, 2, 3] and print results
console.log([...powerSet([1, 2, 3])]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here how you could loop over a very large power set with constant memory and no upper bound (theoretically, there will be an upper bound in terms of compute time) for the array length.
/**
* Generate power set from a given array
* #param {Array<any>} array array to create power set from
*/
function* powerSet(array){
// use BigInt to no longer limited by maximum length of 53-bits
const size = 2n ** BigInt(array.length);
for (let i = 0; i < size; i++) {
const cur = [];
for(let j = 0; j < array.length; j++){
// check if i-th bit is set to 1
if((i & (1 << j)) > 0){
// push array value (represented by that 1-bit) to result
cur.push(array[j]);
}
}
// generate next result
yield cur;
}
}
/**
* Helper function to generate an array containing more than 53 elements
* #param {number} start
* #param {number} end
*/
function* range(start, end){
for (let i = start; i < end; i++) {
yield i;
}
}
// create an array containing elments 1 through 60 ([1, 2, 3, ..., 60])
const oneToSixty = [...range(1, 61)];
let i = 0;
const max = 1000;
// loop over whole powerSet with constant memory requirement
// abort after 1000 subsets, otherwise this will take a very long time to complete
for(const subset of powerSet(oneToSixty)){
console.log(subset);
if(i++ === max) break;
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
I need to get all possible subsets of an array.
Say I have this:
[1, 2, 3]
How do I get this?
[], [1], [2], [3], [1, 2], [2, 3], [1, 3], [1, 2, 3]
I am interested in all subsets. For subsets of specific length, refer to the following questions:
Finding subsets of size n: 1, 2
Finding subsets of size > 1: 1
Here is one more very elegant solution with no loops or recursion, only using the map and reduce array native functions.
const getAllSubsets =
theArray => theArray.reduce(
(subsets, value) => subsets.concat(
subsets.map(set => [value,...set])
),
[[]]
);
console.log(getAllSubsets([1,2,3]));
We can solve this problem for a subset of the input array, starting from offset. Then we recurse back to get a complete solution.
Using a generator function allows us to iterate through subsets with constant memory usage:
// Generate all array subsets:
function* subsets(array, offset = 0) {
while (offset < array.length) {
let first = array[offset++];
for (let subset of subsets(array, offset)) {
subset.push(first);
yield subset;
}
}
yield [];
}
// Example:
for (let subset of subsets([1, 2, 3])) {
console.log(subset);
}
Runtime complexity is proportional to the number of solutions (2ⁿ) times the average length per solution (n/2) = O(n2ⁿ).
Simple solution without recursion:
function getAllSubsets(array) {
const subsets = [[]];
for (const el of array) {
const last = subsets.length-1;
for (let i = 0; i <= last; i++) {
subsets.push( [...subsets[i], el] );
}
}
return subsets;
}
How does it work?
If we have some subsets generated from input numbers and we want to add one more number to our input array, it means that we can take all already existing subsets and generate new ones by appending the new number to each of the existing.
Here is an example for [1, 2, 3]
Start with an empty subset: []
Create new subsets by adding "1" to each existing subset. It will be:[] [1]
Create new subsets by adding "2" to each existing subset. It will be:[], [1] [2], [1, 2]
Create new subsets by adding "3" to each existing subset. It will be: [], [1], [2], [1, 2] [3], [1, 3], [2, 3], [1, 2, 3]
Another simple solution.
function getCombinations(array) {
function fork(i, t) {
if (i === array.length) {
result.push(t);
return;
}
fork(i + 1, t.concat([array[i]]));
fork(i + 1, t);
}
var result = [];
fork(0, []);
return result;
}
var data = [1, 2, 3],
result = getCombinations(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can easily generate the powerset from an array, using something like the following:
var arr = [1, 2, 3];
function generatePowerSet(array) {
var result = [];
result.push([]);
for (var i = 1; i < (1 << array.length); i++) {
var subset = [];
for (var j = 0; j < array.length; j++)
if (i & (1 << j))
subset.push(array[j]);
result.push(subset);
}
return result;
}
console.log(generatePowerSet(arr));
Throughout the main loop of the function, subsets are created and then pushed into the result array.
I set out to understand what is happening with the examples in this post. While the function generator example, bit-wise operator example, and the example use of the array map and reduce functions are very elegant and impressive, I found it tough to mentally visual what precisely was happening. I have 2 examples below that I believe are easy to visualize both a non-recursive and a recursive solution. I hope this helps others attempting to wrap their heads around the process of finding all subsets.
NON-RECURSIVE:
For each value of the array clone all existing subsets (including the empty set) and add the new value to each of the clones, pushing the clones back to the results.
const PowerSet = array => {
const result = [[]] // Starting with empty set
for (let value of array) { // For each value of the array
const length = result.length // Can't use result.length in loop since
// results length is increased in loop
for (let i = 0; i < length; i++){
let temp = result[i].slice(0) // Make a clone of the value at index i
temp.push(value) // Add current value to clone
result.push(temp) // Add clone back to results array
}
}
return result;
}
console.log(PowerSet([1,2,3]))
RECURSIVELY:
Build the powerset by recursively pushing a combination of the current index value concatenated with an ever increasing prefix array of values.
const powerSetRecursive = (arr, prefix=[], set=[[]]) => {
if(arr.length === 0) return// Base case, end recursion
for (let i = 0; i < arr.length; i++) {
set.push(prefix.concat(arr[i]))// If a prefix comes through, concatenate value
powerSetRecursive(arr.slice(i + 1), prefix.concat(arr[i]), set)
// Call function recursively removing values at or before i and adding
// value at i to prefix
}
return set
}
console.log(powerSetRecursive([1,2,3]))
function subSets(num){
/*
example given number : [1,3]
[]
1: copy push 1
[] [1]
3: copy push 3
[] [1] [3] [1,3]
*/
let result = [];
result.push([]);
for(let i=0; i < num.length;i++){
let currentNum = num[i];
let len = result.length;
for(let j=0; j < len; j++){
let cloneData = result[j].slice();
cloneData.push(currentNum);
result.push(cloneData)
}
}
return result;
}
let test = [1,3];
console.log(subSets(test))//[ [], [ 1 ], [ 3 ], [ 1, 3 ] ]
let subsets = (n) => {
let result = [];
result.push([]);
n.forEach(a => {
//array length
let length = result.length;
let i =0;
while(i < length){
let temp = result[i].slice(0);
temp.push(a);
result.push(temp);
i++;
}
})
return result;
}
Using flatMap and rest/spread, this can be fairly elegant:
const subsets = ([x, ...xs]) =>
x == undefined
? [[]]
: subsets (xs) .flatMap (ss => [ss, [x, ...ss]])
console .log (subsets ([1, 2, 3]))
.as-console-wrapper {max-height: 100% !important; top: 0}
This version does not return them in the requested order. Doing that seems slightly less elegant, and there's probably a better version:
const subset = (xs = []) => {
if (xs.length == 0) {return [[]]}
const ys = subset (xs .slice (0, -1))
const x = xs .slice (-1) [0]
return [... ys, ... ys .map (y => [... y, x])]
}
Or, the same algorithm in a different style,
const subsets = (
xs = [],
x = xs .slice (-1) [0],
ys = xs.length && subsets (xs .slice (0, -1))
) =>
xs .length == 0
? [[]]
: [... ys, ... ys .map (y => [... y, x])]
A shorter version of #koorchik's answer.
var getAllSubsets = (nums) => {
const subsets = [[]];
for (n of nums) {
subsets.map((el) => {
subsets.push([...el, n]);
});
}
return subsets;
};
console.log(getAllSubsets([1, 2, 3]));
// [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
For loop:
function powerSet(numbers) {
const subsets = [[]]
for (const number of numbers) {
subsets.forEach(subset => subsets.push([...subset, number]))
}
return subsets
}
Recursion:
function powerSet(numbers) {
const subsets = [[]]
if (numbers.length === 0) return subsets
for (let i = 0; i < numbers.length; i++) {
subsets.push(...powerSet(numbers.slice(i + 1)).map(subset => [numbers[i], ...subset]))
// Or step by step:
// const number = numbers[i]
// const otherNumbers = numbers.slice(i + 1)
// const otherNumbersSubsets = powerSet(otherNumbers)
// const otherNumbersSubsetsWithNumber = otherNumbersSubsets.map(subset => [number, ...subset])
// subsets.push(...otherNumbersSubsetsWithNumber)
}
return subsets
}
Using reduceRight:
const subsets = array =>
array.reduceRight(
(accumulator, a) => [...accumulator, ...accumulator.map(b => [a, ...b])],
[[]]
);
console.log(subsets([1, 2, 3])); // [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
This one is with recursion
var subsets = function(s){
if(s.length === 0) {
return [[]]
}
var h,t,ss_excl_h;
var ss_incl_h = [];
[h,...t] = s;
ss_excl_h = subsets(t)
for(ss of ss_excl_h) {
let hArr = [];
hArr.push(h);
let temp = hArr.concat(ss)
ss_incl_h.push(temp);
}
return ss_incl_h.concat(ss_excl_h)
}
console.log(subsets([1,2,3])) // returns distinct subsets
Update ES2020
With ES2020 BigInts have become available.
Bigints don’t have a fixed storage size in bits; their sizes adapt to the integers they represent.
- Dr. Axel Rauschmayer; JavaScript for impatient programmers - Chapter 18.2 BigInts
See source.
Using BitInts we can use a binary counter to calculate the power set and are no longer limited by the maximum integer size.
Using a generator we can additionally loop over a power set with constant memory requirement which is important if you want to generate a huge power set.
Here an example using you original array [1, 2, 3].
/**
* Generate power set from a given array
* #param {Array<any>} array array to create power set from
*/
function* powerSet(array){
// use BigInt to be no longer limited by maximum length of 53-bits
const size = 2n ** BigInt(array.length);
for (let i = 0; i < size; i++) {
const cur = [];
for(let j = 0; j < array.length; j++){
// check if i-th bit is set to 1
if((i & (1 << j)) > 0){
// push array value (represented by that 1-bit) to result
cur.push(array[j]);
}
}
// generate next result
yield cur;
}
}
// generate power set for [1, 2, 3] and print results
console.log([...powerSet([1, 2, 3])]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here how you could loop over a very large power set with constant memory and no upper bound (theoretically, there will be an upper bound in terms of compute time) for the array length.
/**
* Generate power set from a given array
* #param {Array<any>} array array to create power set from
*/
function* powerSet(array){
// use BigInt to no longer limited by maximum length of 53-bits
const size = 2n ** BigInt(array.length);
for (let i = 0; i < size; i++) {
const cur = [];
for(let j = 0; j < array.length; j++){
// check if i-th bit is set to 1
if((i & (1 << j)) > 0){
// push array value (represented by that 1-bit) to result
cur.push(array[j]);
}
}
// generate next result
yield cur;
}
}
/**
* Helper function to generate an array containing more than 53 elements
* #param {number} start
* #param {number} end
*/
function* range(start, end){
for (let i = start; i < end; i++) {
yield i;
}
}
// create an array containing elments 1 through 60 ([1, 2, 3, ..., 60])
const oneToSixty = [...range(1, 61)];
let i = 0;
const max = 1000;
// loop over whole powerSet with constant memory requirement
// abort after 1000 subsets, otherwise this will take a very long time to complete
for(const subset of powerSet(oneToSixty)){
console.log(subset);
if(i++ === max) break;
}
.as-console-wrapper { max-height: 100% !important; top: 0; }