StFS
StFS

Reputation: 1710

Split list into sub-lists, similar to collate but with a max size of the result

Let's say I have a list

def letters = 'a' .. 'g'

I know that I can use collate to create a list of sub-lists of equal size (plus the remainder).

assert letters.collate(3) == [['a', 'b', 'c'], ['d', 'e', 'f'], ['g']]

But what I want is to get a list of a specific size of sub-lists, where the items of the original list are split into sublists that are as big as needed to get the most equal distribution of the sub-list size. Example:

def numbers = 1..7

assert numbers.collateIntoFixedSizedList(5) == [[1,2], [3,4], [5], [6], [7]]
// the elements that contain two items could be at the end of the list as well
// doesn't matter much to me
assert numbers.collateIntoFixedSizedList(5) == [[1], [2], [3], [4,5], [6,7]]

Lists that are smaller than the max_size would produce a list of the same size as the original of single element lists:

def numbers = 1..7
assert numbers.collateIntoFixedSizeList(10) == [[1],[2],[3],[4],[5],[6],[7]]

Does anybody know whether such magic exists or will I have to code this up myself?

Upvotes: 1

Views: 878

Answers (1)

tim_yates
tim_yates

Reputation: 171084

There's nothing built in to Groovy to do this, but you could write your own:

def fancyCollate(Collection collection, int groupCount) {
    collection.indexed().groupBy { i, v -> i % groupCount }.values()*.values()
}

Or, you could do this, which creates less intermediate objects:

def fancyCollate(Collection collection, int groupCount) {
    (0..<collection.size()).inject([[]] * groupCount) { l, v ->
        l[v % groupCount] += collection[v]
        l
    }
}

Try #2 ;-)

def fancyCollate(Collection collection, int size) {
    int stride = Math.ceil((double)collection.size() / size)
    (1..size).collect { [(it - 1) * stride, Math.min(it * stride, collection.size())] }
        .collect { a, b -> collection.subList(a, b) }
}

assert fancyCollate('a'..'z', 3) == ['a'..'i', 'j'..'r', 's'..'z']

Try #3 (with your example)

Collection.metaClass.collateIntoFixedSizeList = { int size ->
    int stride = Math.ceil((double)delegate.size() / size)
    (1..Math.min(size, delegate.size())).collect { [(it - 1) * stride, Math.min(it * stride, delegate.size())] }
        .collect { a, b -> delegate.subList(a, b) }
}

def numbers = (1..7)
assert numbers.collateIntoFixedSizeList(10) == [[1],[2],[3],[4],[5],[6],[7]]

Upvotes: 3

Related Questions