Reputation: 1089
I have two lists lets say:
def list1 = [a,b,c,d,e...]
def list2 = [1,2,3,4,5... ]
I want them to mix in a certain pattern so that final list looks like:
[a,b,c,d,1,2,e,f,g,h,3,4,i,j,k,l,5,6...]
Basically after every n
elements from list1, i get m
elements from list2.
EDIT: If one list runs out of elements, the items in the remaining list should simply get added to the final list.
EDIT2: Both the lists can have objects as elements.
I want to find the most efficient way to solve this problem.
Upvotes: 4
Views: 952
Reputation: 261
I have a functional solution preserving remaining elements based on the main answer.
List mix( Map<Integer,List> amounts ) {
def maxItems = amounts.collect { k, v -> v.size() }.max()
amounts.collect { k, v ->
def padding = maxItems - v.size()
it.addAll((1..padding).collect { null })
v.collate( k )
}.transpose().flatten().grep { it }
}
I tested it in a simplified version to mix three lists taking one item at time. The idea is just adding padding with null objects to make all of the same length.
Upvotes: 0
Reputation: 171084
Here's one way of doing this in Groovy:
So I have a method mix
which takes a map with Integer keys (the number of elements required), and Lists as values:
List mix( Map<Integer,List> amounts ) {
amounts.collect { k, v ->
v.collate( k )
}.transpose().flatten()
}
Then, given:
// The letters a to z
def list1 = 'a'..'z'
// The numbers 1 to 10
def list2 = 1..10
// Call, and ask for 4 of list1 followed by 2 of list2
mix( [ 4:list1, 2:list2 ] )
That returns:
[ 'a', 'b', 'c', 'd', 1, 2,
'e', 'f', 'g', 'h', 3, 4,
'i', 'j', 'k', 'l', 5, 6,
'm', 'n', 'o', 'p', 7, 8,
'q', 'r', 's', 't', 9, 10 ]
(formatted to look better here)
As you can see, it runs out of numbers first, and when it does, the list ends. This is because transpose stops when one list runs out of elements.
Worked out another way with Iterators (so it's lazy and won't use up more memory than is otherwise required):
class MixingIterator<T> implements Iterator<T> {
private int idx = 0
private List<Iterator> iter
private List<Integer> amts
MixingIterator( List<List> lists, List<Integer> amounts ) {
iter = lists*.iterator()
int i = 0
amts = amounts.collectMany { [ i++ ] * it }
// OR FOR GROOVY 1.7.8
// amts = amounts.collect { [ i++ ] * it }.flatten()
}
private void moveIdx() {
idx = ++idx % amts.size()
}
@Override boolean hasNext() {
iter*.hasNext().any()
}
@Override T next() {
if( !hasNext() ) { throw new NoSuchElementException() }
while( !iter[ amts[ idx ] ].hasNext() ) { moveIdx() }
T ret = iter[ amts[ idx ] ].next()
moveIdx()
ret
}
@Override void remove() {
throw new UnsupportedOperationException()
}
}
You call it by:
def list1 = 'a'..'z'
def list2 = 1..10
def ret = new MixingIterator( [ list1, list2 ], [ 4, 2 ] ).collect()
// OR FOR GROOVY 1.7.8
// def ret = new MixingIterator( [ list1, list2 ], [ 4, 2 ] ).collect { it }
And ret will then equal:
['a', 'b', 'c', 'd', 1, 2,
'e', 'f', 'g', 'h', 3, 4,
'i', 'j', 'k', 'l', 5, 6,
'm', 'n', 'o', 'p', 7, 8,
'q', 'r', 's', 't', 9, 10,
'u', 'v', 'w', 'x', 'y', 'z']
Upvotes: 5