-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathutil.js
More file actions
141 lines (130 loc) · 4.64 KB
/
util.js
File metadata and controls
141 lines (130 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
* Lodash mixins for combinatorics
* Inspired by python itertools: https://docs.python.org/2.7/library/itertools.html
*
* Usage:
* permutations([0,1,2],2) // [[0,1],[0,2],[1,0],[1,2],[2,0],[2,1]]
* combinations([0,1,2],2) // [[0,1],[0,2],[1,2]]
* combinations_with_replacement([0,1,2],2)// [[0,0],[0,1],[0,2],[1,1],[1,2],[2,2]]
* product([0,1,2],[0,1,2]) // [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2]]
*
* Multiple input types:
* product('me','hi')
* product({who:['me','you'],say:['hi','by']})
* product(['me','you'],['hi','by'])
* product(['me','hi'])
* combinations([0,1,2,3],2)
* permutations([1,2,3],2)
* permutations('cat',2)
*/
var _ = require('lodash')
/**
* Generate all combination of arguments when given arrays or strings
* e.g. [['Ben','Jade','Darren'],['Smith','Miller']] to [['Ben','Smith'],[..]]
* e.g. 'the','cat' to [['t', 'c'],['t', 'a'], ...]
**/
function _cartesianProductOf(args) {
if (arguments.length>1) args=_.toArray(arguments);
// strings to arrays of letters
args=_.map(args, opt=>typeof opt==='string'?_.toArray(opt):opt)
return _.reduce(args, function(a, b) {
return _.flatten(_.map(a, function(x) {
return _.map(b, function(y) {
return _.concat(x,[y]);
});
}), true);
}, [ [] ]);
}
/** Generate all combination of arguments from objects
* {Object} opts - An object or arrays with keys describing options {firstName:['Ben','Jade','Darren'],lastName:['Smith','Miller']}
* {Array} - An array of objects e.g. [{firstName:'Ben',LastName:'Smith'},{..]
**/
function _cartesianProductObj(optObj){
var keys = _.keys(optObj);
var opts = _.values(optObj);
var combs = _cartesianProductOf(opts);
return _.map(combs,function(comb){
return _.zipObject(keys,comb);
});
}
/**
* Generate the cartesian product of input objects, arrays, or strings
*
*
* product('me','hi')
* // => [["m","h"],["m","i"],["e","h"],["e","i"]]
*
* product([1,2,3],['a','b','c']
* // => [[1,"a"],[1,"b"],[1,"c"],[2,"a"],[2,"b"],[2,"c"],[3,"a"],[3,"b"],[3,"c"]]
*
* product({who:['me','you'],say:['hi','by']})
* // => [{"who":"me","say":"hi"},{"who":"me","say":"by"},{"who":"you","say":"hi"},{"who":"you","say":"by"}]
*
* // It also takes in a single array of args
* product(['me','hi'])
* // => [["m","h"],["m","i"],["e","h"],["e","i"]]
*/
function product(opts){
if (arguments.length===1 && !_.isArray(opts))
return _cartesianProductObj(opts)
else if (arguments.length===1)
return _cartesianProductOf(opts)
else
return _cartesianProductOf(arguments)
}
/**
* Generate permutations, in all possible orderings, with no repeat values
*
*
* permutations([1,2,3],2)
* // => [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]
*
* permutations('cat',2)
* // => [["c","a"],["c","t"],["a","c"],["a","t"],["t","c"],["t","a"]]
*/
function permutations(obj, n){
if (typeof obj=='string') obj = _.toArray(obj)
n = n?n:obj.length
// make n copies of keys/indices
for (var j = 0, nInds=[]; j < n; j++) {nInds.push(_.keys(obj)) }
// get product of the indices, then filter to remove the same key twice
var arrangements = product(nInds).filter(pair=>pair[0]!==pair[1])
return _.map(arrangements,indices=>_.map(indices,i=>obj[i]))
}
/**
* Generate n combinations of an object with no repeat values in each combination.
*
*
* combinations([0,1,2,3],2)
* // => [[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]]
*/
function combinations(obj,n){
/* filter out keys out of order, e.g. [0,1] is ok but [1,0] isn't */
function isSorted(arr) {
return _.every(arr, function (value, index, array) {
return index === 0 || String(array[index - 1]) <= String(value);
});
}
// array with n copies of the keys of obj
return _(permutations(_.keys(obj),n))
.filter(isSorted)
.map(indices=>_.map(indices,i=>obj[i]))
.value()
}
/**
* Generate n combinations with repeat values.
*
*
* combinations_with_replacement([0,1,2,3],2)
* // => [[0,0],[0,1],[0,2],[0,3],[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]]
*/
function combinations_with_replacement(obj,n){
if (typeof obj=='string') obj = _.toArray(obj)
n = n?n:obj.length
// make n copies of keys/indices
for (var j = 0, nInds=[]; j < n; j++) {nInds.push(_.keys(obj)) }
// get product of the indices, then filter to keep elements in order
var arrangements = product(nInds).filter(pair=>pair[0]<=pair[1])
return _.map(arrangements,indices=>_.map(indices,i=>obj[i]))
}
module.exports={combinations_with_replacement,combinations,product,permutations}