Every element should be paired with each other but only once.
Given a list A, B, C I want to make the following list of pairs: (A,B), (A,C), (B,C)
Similarly for 4 elements A, B, C, D the result should be (A,B), (A,C), (A,D), (B,C), (B,D), (C,D).
I tried with eachPermutation, eachCombintation but couldn't find a nice way. It would be a big help if you would tell me what's the matemathical name for this operation.
A little late but seems subsequences would solve this quickly. It provides more than pairs, but limiting the result set would be trivial and obvious to readers in the future:
def pairs(def l) {
l.subsequences().findAll {it.size() == 2}
}
assert pairs(['a','b','c']) == [['a','b'], ['a','c'], ['b', 'c']] as Set
assert pairs(['a', 'b', 'c', 'd']) == [['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']] as Set
There's probably no such a feature in Groovy, but you can implement it quite easily:
def pairs(def elements) {
return elements.tail().collect { [elements.head(), it] } + (elements.size() > 1 ? pairs(elements.tail()) : [])
}
assert pairs(['A', 'B', 'C']) == [['A', 'B'], ['A', 'C'], ['B', 'C']]
assert pairs(['A', 'B', 'C', 'D']) == [['A', 'B'], ['A', 'C'], ['A', 'D'], ['B', 'C'], ['B', 'D'], ['C', 'D']]
You can use a combinations, toSet, sort and findAll the remaining whose size equals 2:
def uniqueCombinations = { l ->
[l,l].combinations()*.toSet()*.sort().unique().findAll { it.size() == 2 }
}
l=[1,2,3]
assert uniqueCombinations(l) == [[1, 2], [1, 3], [2, 3]]
With eachCombination it would be:
def l = ['A', 'B', 'C', 'D'], result = []
[l, l].eachCombination {
if ( ! ( it in result*.intersect( it ) || it[0] == it[1] ) ) {
result << it.reverse()
}
}
assert result == [
['A', 'B'], ['A', 'C'], ['A', 'D'], ['B', 'C'], ['B', 'D'], ['C', 'D']
]
how about doing it recursive? combinnations of the first element and the tail and then recurse on the rest of the list.
def comb(l,r=[]) {
if (l) {
r.addAll( [l.head(), l.tail()].combinations() )
return comb(l.tail(), r)
}
return r
}
def m = ['a','b','c','d']
def result = comb(m)
assert result == [['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]
Related
I want to generate different combinations of 3 elements a, b, and c. The length of these combinations needs to be 4. I want to have a maximum of 4 times from 'a' and a maximum 1 time from each 'b' and 'c' element. So, for example, we can have ['a',' a',' a','a'] or ['a','a','b','c'] but not ['a','b','b','b'].
There is a similar question in 1, but, as far as I know, using the last 'gen' function, the length of a generation is controlled by the multiplication of a maximum number of repetitions (4 in my case). Also, cases were limited to tuples with exactly 1 'b' and 1 'c' and the rest are 'a'. For the last issue, I replaced 'combinations' with 'combinations_with_replacement', but it still produces tuples with 4 elements and there is no ['a',' a',' a','a'].
How can I tackle this problem?
Here is the code:
from itertools import combinations_with_replacement
def gen(ns, elems=None, C=None, out=None):
if elems is None:
elems = list(range(len(ns)))
else:
assert len(elems) == len(ns)
if out is None:
N = 1
for n in ns:
N *= n
out = [elems[0]]*N
C = range(N)
if len(ns) == 1:
yield out
else:
n = ns[-1]
e = elems[-1]
for c in combinations_with_replacement(C,n):
out_ = out.copy()
for i in c:
out_[i] = e
C_ = [i for i in C if i not in c]
yield from gen(ns[:-1], elems[:-1], C_, out_)
for tmp_list in gen([4,1,1], elems=['a', 'b', 'c'], C=None, out=None):
print(tmp_list)
output:
['c', 'b', 'a', 'a']
['c', 'a', 'b', 'a']
['c', 'a', 'a', 'b']
['b', 'c', 'a', 'a']
['a', 'c', 'b', 'a']
['a', 'c', 'a', 'b']
['b', 'a', 'c', 'a']
['a', 'b', 'c', 'a']
['a', 'a', 'c', 'b']
['b', 'a', 'a', 'c']
['a', 'b', 'a', 'c']
['a', 'a', 'b', 'c']
Please note that since I care about the execution time, I want to generate the tuples in a loop.
I am looking for an algorithm that would find all possible combinations of list elements without using itertools.
eg. : for [1,2,3,4,5] it prints [[1],[2],[3],[4],[5],[1,2],[1,3],[1,4],[1,5],[2,1].......]
A solution is presented here using recursion.
>>> import copy
>>> def combinations(target,data):
... for i in range(len(data)):
... new_target = copy.copy(target)
... new_data = copy.copy(data)
... new_target.append(data[i])
... new_data = data[i+1:]
... print new_target
... combinations(new_target,
... new_data)
...
...
>>> target = []
>>> data = ['a','b','c','d']
>>>
>>> combinations(target,data)
['a']
['a', 'b']
['a', 'b', 'c']
['a', 'b', 'c', 'd']
['a', 'b', 'd']
['a', 'c']
['a', 'c', 'd']
['a', 'd']
['b']
['b', 'c']
['b', 'c', 'd']
['b', 'd']
['c']
['c', 'd']
['d']
I try to add a list to a dictionary key, but when I append a value, it returns the value None. I have also tried collections.defaultdict(list) without success.
Code:
text = "ABBBAACCCCAABBCCCCAABCBCBCABCCCA"
chain = dict()
for i in range (0, text.__len__()-1):
key = text[i : i+1]
next_word = text[i +1 : i +2]
if key not in chain.keys():
chain.setdefault(key)
else:
chain.setdefault(key, [].append(next_word))
print(key, next_word, chain[key], chain)
Output:
A B None {'A': None}
B B None {'B': None, 'A': None}
B B None {'B': None, 'A': None}
B A None {'B': None, 'A': None}
…
[].append() returns None. You'd want to append to the return value of dict.setdefault() instead. You also don't need to do a key containment test when using dict.setdefault(); setdefault() already makes that test for you.
Next, don't call object.__len__(). Use len(object) instead. I'd also use {} instead of dict(); the latter has to look up a name and make a function call, the {} literal is compiled to a single bytecode to create a dictionary.
This works:
for i in range(len(text) - 1):
key = text[i:i + 1]
next_word = text[i + 1:i + 2]
chain.setdefault(key, []).append(next_word)
You could also use zip() to pair up the letters:
for key, next_word in zip(text, text[1:]):
chain.setdefault(key, []).append(next_word)
Demo:
>>> text = "ABBBAACCCCAABBCCCCAABCBCBCABCCCA"
>>> chain = {}
>>> for key, next_word in zip(text, text[1:]):
... chain.setdefault(key, []).append(next_word)
...
>>> chain
{'A': ['B', 'A', 'C', 'A', 'B', 'A', 'B', 'B'], 'B': ['B', 'B', 'A', 'B', 'C', 'C', 'C', 'C', 'C'], 'C': ['C', 'C', 'C', 'A', 'C', 'C', 'C', 'A', 'B', 'B', 'A', 'C', 'C', 'A']}
>>> from pprint import pprint
>>> pprint(chain)
{'A': ['B', 'A', 'C', 'A', 'B', 'A', 'B', 'B'],
'B': ['B', 'B', 'A', 'B', 'C', 'C', 'C', 'C', 'C'],
'C': ['C', 'C', 'C', 'A', 'C', 'C', 'C', 'A', 'B', 'B', 'A', 'C', 'C', 'A']}
I have a large number of identical lists 'old' which I want to transform in the same way into a list 'new'. The way I want to do it, is to make an example of the desired list 'new'. Then I turn the difference between the two lists 'old' and 'new' into a rule, and then use that rule to turn my other lists 'old_2' into 'new_2'.
I cannot figure out how to do the first step and the second step does not give me the expected result. Is there an elegant way to do this?
import numpy
# 0 1 2 3 4 5
old_1 = ['A', 'B', 'C', 'D', 'E', 'F']
new = ['B', 'C', 'D', 'E', 'A']
# 01 Get the difference new - /- old_1 based on index positions of
# the list elements, to get something like this:
order = [1,2,3,4,0]
# 02 Then use this order to transform a second identical list, old_2.
# For this I wanted to use the following:
old_2 = ['A', 'B', 'C', 'D', 'E', 'F']
old_2 = numpy.array(old_2)
order = numpy.array(order)
inds = order.argsort()
print('inds =', inds) # As a check, this gives the wrong order: [4 1 0 2 3]
new_2 = old_2[inds]
# I expected this to result in what I want, which is:
print(new_2)
['C', 'B', 'D', 'E', 'A']
# But what I get in reality is this:
inds = [4 1 0 2 3]
['E' 'B' 'A' 'C' 'D']
Any suggestions to get the desired result?
new_2 = ['B', 'C', 'D', 'E', 'A']
From what I understand, I tried to edit your code. Hopefully it helps.
import numpy as np
def get_order(new, old):
order = []
for element in new:
order.append(old.index(element))
return order
def main():
old_1 = ['A', 'B', 'C', 'D', 'E', 'F']
new = ['B', 'C', 'D', 'E', 'A']
order = get_order(new, old_1)
print(order)
old_2 = ['A', 'B', 'C', 'D', 'E', 'F']
old_2 = np.array(old_2)
order = np.array(order)
#inds = order.argsort()
#print('inds =', inds) # As a check, this gives the wrong order: [4 1 0 2 3]
new_2 = old_2[order]
print(new_2)
if __name__ == '__main__':
main()
Output
[1, 2, 3, 4, 0]
['B' 'C' 'D' 'E' 'A']
Is there method or some smart way that's easy to read to make a combination of elements in Groovy? I'm aware of Iterable#combinations or GroovyCollections#combinations but it makes Partial permutation with repetition as I understand it so far. See example.
// Groovy combinations result
def e = ['a', 'b', 'c']
def result = [e, e].combinations()
assert [['a', 'a'], ['b', 'a'], ['c', 'a'], ['a', 'b'], ['b', 'b'], ['c', 'b'], ['a','c'], ['b', 'c'], ['c', 'c']] == result
// What I'm looking for
def e = ['a', 'b', 'c']
def result = ???
assert [['a', 'b'], ['a', 'c'], ['b', 'c']] == result
Feel free to post alternate solutions. I'm still looking for better readability (it's used in script for non-developers) and performance (w/o unnecessary iterations).
I'm not so sure about the readability, but this should do the trick.
def e = ['a', 'b', 'c']
def result = [e, e].combinations().findAll { a, b ->
a < b
}
assert [['a', 'b'], ['a', 'c'], ['b', 'c']] == result
Note that if a element occur twice in the list its combinations will also occur twice. Add a '.unique()' at the end if they are unwanted
Here's a more generalized approach that allows you to specify the "r" value for your nCr combinations. It does this by storing permutations in Sets, with the Sets providing the uniqueness:
// returns combinations of the input list of the provided size, r
List combinationsOf(List list, int r) {
assert (0..<list.size()).contains(r) // validate input
def combs = [] as Set
list.eachPermutation {
combs << it.subList(0, r).sort { a, b -> a <=> b }
}
combs as List
}
// the test scenario...
def e = ['a', 'b', 'c']
def result = combinationsOf(e, 2)
assert [['a', 'b'], ['a', 'c'], ['b', 'c']] == result