Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
JavaScript functions to calculate combinations of elements in Array.
/**
* Copyright 2012 Akseli Palén.
* Created 2012-07-15.
* Licensed under the MIT license.
*
* <license>
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* </lisence>
*
* Implements functions to calculate combinations of elements in JS Arrays.
*
* Functions:
* k_combinations(set, k) -- Return all k-sized combinations in a set
* combinations(set) -- Return all combinations of the set
*/
/**
* K-combinations
*
* Get k-sized combinations of elements in a set.
*
* Usage:
* k_combinations(set, k)
*
* Parameters:
* set: Array of objects of any type. They are treated as unique.
* k: size of combinations to search for.
*
* Return:
* Array of found combinations, size of a combination is k.
*
* Examples:
*
* k_combinations([1, 2, 3], 1)
* -> [[1], [2], [3]]
*
* k_combinations([1, 2, 3], 2)
* -> [[1,2], [1,3], [2, 3]
*
* k_combinations([1, 2, 3], 3)
* -> [[1, 2, 3]]
*
* k_combinations([1, 2, 3], 4)
* -> []
*
* k_combinations([1, 2, 3], 0)
* -> []
*
* k_combinations([1, 2, 3], -1)
* -> []
*
* k_combinations([], 0)
* -> []
*/
function k_combinations(set, k) {
var i, j, combs, head, tailcombs;
// There is no way to take e.g. sets of 5 elements from
// a set of 4.
if (k > set.length || k <= 0) {
return [];
}
// K-sized set has only one K-sized subset.
if (k == set.length) {
return [set];
}
// There is N 1-sized subsets in a N-sized set.
if (k == 1) {
combs = [];
for (i = 0; i < set.length; i++) {
combs.push([set[i]]);
}
return combs;
}
// Assert {1 < k < set.length}
// Algorithm description:
// To get k-combinations of a set, we want to join each element
// with all (k-1)-combinations of the other elements. The set of
// these k-sized sets would be the desired result. However, as we
// represent sets with lists, we need to take duplicates into
// account. To avoid producing duplicates and also unnecessary
// computing, we use the following approach: each element i
// divides the list into three: the preceding elements, the
// current element i, and the subsequent elements. For the first
// element, the list of preceding elements is empty. For element i,
// we compute the (k-1)-computations of the subsequent elements,
// join each with the element i, and store the joined to the set of
// computed k-combinations. We do not need to take the preceding
// elements into account, because they have already been the i:th
// element so they are already computed and stored. When the length
// of the subsequent list drops below (k-1), we cannot find any
// (k-1)-combs, hence the upper limit for the iteration:
combs = [];
for (i = 0; i < set.length - k + 1; i++) {
// head is a list that includes only our current element.
head = set.slice(i, i + 1);
// We take smaller combinations from the subsequent elements
tailcombs = k_combinations(set.slice(i + 1), k - 1);
// For each (k-1)-combination we join it with the current
// and store it to the set of k-combinations.
for (j = 0; j < tailcombs.length; j++) {
combs.push(head.concat(tailcombs[j]));
}
}
return combs;
}
/**
* Combinations
*
* Get all possible combinations of elements in a set.
*
* Usage:
* combinations(set)
*
* Examples:
*
* combinations([1, 2, 3])
* -> [[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
*
* combinations([1])
* -> [[1]]
*/
function combinations(set) {
var k, i, combs, k_combs;
combs = [];
// Calculate all non-empty k-combinations
for (k = 1; k <= set.length; k++) {
k_combs = k_combinations(set, k);
for (i = 0; i < k_combs.length; i++) {
combs.push(k_combs[i]);
}
}
return combs;
}
@agrimnigam21

This comment has been minimized.

Copy link

agrimnigam21 commented Nov 6, 2013

I think it doesnt work for 4 elements

@hardcoremore

This comment has been minimized.

Copy link

hardcoremore commented Nov 8, 2013

It works great. Thanks. I tested it for 4 elements as well and it works great. Thanks.

@chrisgoddard

This comment has been minimized.

Copy link

chrisgoddard commented Aug 9, 2014

So it works but only generates combinations in the same original order - how could you get all combinations in any order?

@ghost

This comment has been minimized.

Copy link

ghost commented Aug 18, 2014

thanks a lot

@skibulk

This comment has been minimized.

Copy link

skibulk commented Sep 10, 2015

@chrisgoddard you're looking for a permutations function, not a combinations function.

@danbars

This comment has been minimized.

Copy link

danbars commented Sep 28, 2015

@chrisgoddard you can run each combination through a permutation algorithm with something like this: https://github.com/eriksank/permutation-engine
Or, use permutation/combination engine, like this one:
https://github.com/dankogai/js-combinatorics

@rhalff

This comment has been minimized.

Copy link

rhalff commented Feb 23, 2016

Is the above gist available on npm somewhere? I want to use it in my library, right now I've just copy & pasted it.

@sallar

This comment has been minimized.

Copy link

sallar commented Feb 24, 2016

@danbars Thanks for the links. +1

@foluis

This comment has been minimized.

Copy link

foluis commented Aug 5, 2016

Does Not work for this set "ababb" it repeats elements and also "abbba", "babab" are missing

@ComethTheNerd

This comment has been minimized.

Copy link

ComethTheNerd commented Oct 25, 2016

@foluis all member elements must be considered unique in order to form a set mathematically

@imVinayPandya

This comment has been minimized.

Copy link

imVinayPandya commented Mar 31, 2017

possible combination of 1, 2, 3 are following,may be i am wrong

[1], [2], [3], [1, 2], [1, 3], [2, 3], [2, 1], [3, 1], [3, 2], [1, 2, 3], [3, 2, 1]

@douglasFranco

This comment has been minimized.

Copy link

douglasFranco commented Apr 2, 2017

Thanks a lot!

@ragamufin

This comment has been minimized.

Copy link

ragamufin commented May 5, 2017

Thanks!

@t1mduma5

This comment has been minimized.

Copy link

t1mduma5 commented Jul 22, 2017

@imVinayPandya Looks like he is using N choose K which should only return unique combinations. Such as 1,2 and 2,1 are the same. Also, you are setting the number of members for the k combination. Thus this will only return single member combinations, or double member combinations or triple member combinations etc etc etc.

@regevbr

This comment has been minimized.

Copy link

regevbr commented Feb 20, 2018

thanks!

@regevbr

This comment has been minimized.

Copy link

regevbr commented Feb 20, 2018

Here is a short hand version using underscore and some es6 syntax for readability

"use strict";
//https://gist.github.com/axelpale/3118596

const _ = require('underscore');

const elemTransform = elem => [elem];
const tailcombPush = (combs, elem) => tailcomb => combs.push([elem, ...tailcomb]);
const k_combPush = combs => k_komb => combs.push(k_komb);

function k_combinations(set, k) {
  const setLen = set.length;
  if (k > setLen || k <= 0) {
    return [];
  }
  if (k === setLen) {
    return [set];
  }
  if (k === 1) {
    return _.map(set, elemTransform);
  }
  const combs = [];
  for (let i = 0; i < setLen - k + 1; i++) {
    _.each(k_combinations(set.slice(i + 1), k - 1), tailcombPush(combs, set[i]));
  }
  return combs;
}

function combinations(set) {
  const combs = [];
  for (let k = 1; k <= set.length; k++) {
    _.each(k_combinations(set, k), k_combPush(combs));
  }
  return combs;
}

module.exports = {
  k_combinations,
  combinations
};
@morenoh149

This comment has been minimized.

Copy link

morenoh149 commented Feb 28, 2018

Here's an es6 version

const k_combinations = (set, k) => {                                            
  if (k > set.length || k <= 0) {                                               
    return []                                                                   
  }                                                                             
  if (k === set.length) {                                                       
    return [set]                                                    
  }                                                                             
  const combs = []                                                              
  if (k === 1) {                                                                
    for (let i = 0; i < set.length; i++) {                                      
      combs.push([set[i]])                                                      
    }                                                                           
    return combs                                                                
  }                                                                             
  for (let i = 0; i < set.length - k + 1; i++) {                                
    const head = set.slice(i, i + 1)                                            
    const tailcombs = k_combinations(set.slice(i + 1), k - 1)              
    for (let j = 0; j < tailcombs.length; j++) {                                
      combs.push(head.concat(tailcombs[j]))                                     
    }                                                                           
  }                                                                             
  return combs                                                                  
}                                                                               
                                                                                
const combinations = (set) => {                                                 
  const combs = [];                                                             
  for (let k = 1; k <= set.length; k++) {                                       
    const k_combs = k_combinations(set, k)                                      
    for (let i = 0; i < k_combs.length; i++) {                                  
      combs.push(k_combs[i])                                                    
    }                                                                           
  }                                                                             
  return combs                                                                  
}
@luispaulorsl

This comment has been minimized.

Copy link

luispaulorsl commented Nov 5, 2018

ES6 version:

const k_combinations = (set, k) => {
  if (k > set.length || k <= 0) {
    return []
  }
  
  if (k == set.length) {
    return [set]
  }
  
  if (k == 1) {
    return set.reduce((acc, cur) => [...acc, [cur]], [])
  }
  
  let combs = [], tail_combs = []
  
  for (let i = 0; i <= set.length - k + 1; i++) {
    tail_combs = k_combinations(set.slice(i + 1), k - 1)
    for (let j = 0; j < tail_combs.length; j++) {
      combs.push([set[i], ...tail_combs[j]])
    }
  }
  
  return combs
}

const combinations = set => {
  return set.reduce((acc, cur, idx) => [...acc, ...k_combinations(set, idx + 1)], [])
}
@cardamon

This comment has been minimized.

Copy link

cardamon commented Dec 10, 2019

And here is a (generic) typescript version, based on the ES6 version by @luispaulorsl:

export const k_combinations = <T>(set: T[], k: number) => {
    if (k > set.length || k <= 0) {
        return [];
    }

    if (k === set.length) {
        return [set];
    }

    if (k === 1) {
        return set.reduce((acc, cur) => [...acc, [cur]], [] as T[][]);
    }

    const combs = [] as T[][];
    let tail_combs = [];

    for (let i = 0; i <= set.length - k + 1; i++) {
        tail_combs = k_combinations(set.slice(i + 1), k - 1);
        for (let j = 0; j < tail_combs.length; j++) {
            combs.push([set[i], ...tail_combs[j]]);
        }
    }

    return combs;
};

export const combinations = <T>(set: T[]) => {
    return set.reduce((acc, _cur, idx) => [...acc, ...k_combinations(set, idx + 1)], [] as T[][]);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.