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

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?

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]]

Related

what is the difference between findResults and collect in groovy?

Here is the code using collect
​def lst = [1,2,3,4];
def newlst = [];
newlst = lst.collect {element -> return element * element}
println(newlst);
Here is the code using findResults
def lst2 = [1,2,3,4];
def newlst2 = [];
newlst2 = lst2.findResults {element -> return element * element}
println(newlst2);
​Both seem to return [1, 4, 9, 16] so what is the difference? Thanks!
Basically the difference is how they deal with null values
collect when sees null will collect it, while findResults won't pick it.
In other words, the size of resulting collection is the same as the size of input when using collect.
Of course you could filter out the results but its an additional step
Here is a link to the example I've found in the internet
Example:
​def list = [1, 2, 3, 4]
println list.coll​​​​​​​​​​​​​​ect { it % 2 ? it : null}
// [1, null, 3, null]
println list.findResults { it % 2 ? it : null}​
// [1,3]
When we need to check if returned list is empty then findResults seem more useful. Thanks to Mark for the answer.
def list = [1, 2, 3, 4]
def l1 = list.collect { it % 100 == 0 ? it : null}
def l2 = list.findResults { it % 100 == 0 ? it : null}
if(l1){
println("not null/empty " + l1)
}
if(l2){
println("not null/empty " + l2)
}
​

Groovy or Java equivalent of sumproduct?

Before I writing my own, does anyone know if Groovy or Java has something pre-built which is similar to Excel's sumproduct function?
The quasi syntax for sumproduct is something like
def list1 = [2,3,4]
def list2 = [5,10,20]
SUMPRODUCT(list1, list2 ...) = 120
You will get 120 ((2*5) + (3*10) + (4*20) = 120)
You can transpose(), collect() and sum the result:
def list1 = [2,3,4]
def list2 = [5,10,20]
assert [list1, list2]
.transpose()
.collect { it[0] * it[1] }
.sum() == 120
not really the out-of-box SUMPRODUCT substitute, but still an one-liner:
def list1 = [2,3,4]
def list2 = [5,10,20]
assert 120 == GroovyCollections.transpose( list1, list2 ).sum{ it[ 0 ] * it[ 1 ] }
Here's a version of sumproduct that isn't limited to two input lists:
def sumproduct(List... lists) {
(lists as List).transpose().sum { it.inject(1) { prod, val -> prod * val } }
}
Calling it with sumproduct([2,3,4], [5,10,20], [1,2,3]) returns 310.

How do I implement a comparator for a map in Groovy?

I have a map in Groovy:
['keyOfInterest' : 1, 'otherKey': 2]
There is a list containing a number of these maps. I want to know if a map exists in the list with keyOfInterest of a certain value.
If the data types were simple objects, I could use indexOf(), but I don't know how to do this with a more complicated type. E.g. (taken from the docs)
assert ['a', 'b', 'c', 'd', 'c'].indexOf('z') == -1 // 'z' is not in the list
I'd like to do something like:
def mapA = ['keyOfInterest' : 1, 'otherKey': 2]
def mapB = ['keyOfInterest' : 3, 'otherKey': 2]
def searchMap = ['keyOfInterest' : 1, 'otherKey': 5]
def list = [mapA, mapB]
assert list.indexOf(searchMap) == 0 // keyOfInterest == 1 for both mapA and searchMap
Is there a way to do this with more complicated objects, such as a map, easily?
While #dmahapatro is correct, and you can use find() to find the map in the list of maps that has the matching index... that's not what you asked for. So I'll show how you can get either the index of that entry in the list, or just whether a map with matching keyOfInterest exists.
def mapA = ['keyOfInterest' : 1, 'otherKey': 2]
def mapB = ['keyOfInterest' : 3, 'otherKey': 2]
def searchMap = ['keyOfInterest':1, 'otherKey': 55 ]
def list = [mapA, mapB]
// findIndexOf() returns the first index of the map that matches in the list, or -1 if none match
assert list.findIndexOf { it.keyOfInterest == searchMap.keyOfInterest } == 0
assert list.findIndexOf { it.keyOfInterest == 33 } == -1
// any() returns a boolean OR of all the closure results for each entry in the list.
assert list.any { it.keyOfInterest == searchMap.keyOfInterest } == true
assert list.any { it.keyOfInterest == 33 } == false
Note that there is no performance penalty for using one over the other as they all stop as soon as one match is found. find() gives you the most information, but if you're actually looking for the index or a boolean result, these others can also be used.
Simplest implementation would be to use find(). It returns null when criteria is not met in the supplied closure.
def mapA = ['keyOfInterest' : 1, 'otherKey': 2]
def mapB = ['keyOfInterest' : 3, 'otherKey': 2]
def list = [mapA, mapB]
assert list.find { it.keyOfInterest == 1 } == ['keyOfInterest':1, 'otherKey':2]
assert !list.find { it.keyOfInterest == 7 }

How do I loop through a list and remove an item in groovy?

I'm trying to figure out how to remove an item from a list in groovy from within a loop.
static main(args) {
def list1 = [1, 2, 3, 4]
for(num in list1){
if(num == 2)
list1.remove(num)
}
println(list1)
}
list = [1, 2, 3, 4]
newList = list.findAll { it != 2 }
Should give you all but the 2
Of course you may have a reason for requiring the loop?
If you want to remove the item with index 2, you can do
list = [1,2,3,4]
list.remove(2)
assert list == [1,2,4]
// or with a loop
list = [1,2,3,4]
i = list.iterator()
2.times {
i.next()
}
i.remove()
assert list == [1,2,4]
If you want to remove the (first) item with value 2, you can do
list = [1,2,3,4]
list.remove(list.indexOf(2))
assert list == [1,3,4]
// or with a loop
list = [1,2,3,4]
i = list.iterator()
while (i.hasNext()) {
if (i.next() == 2) {
i.remove()
break
}
}
assert list == [1,3,4]
As you state in your comment that you do not specifically require a loop .... If you are happy to modify your original list you can use removeAll:
// Remove all negative numbers
list = [1, 2, -4, 8]
list.removeAll { it < 0 }
I think you can do:
list - 2;
or...
list.remove(2)
There's no loop required.
If you want to use a loop I guess you could look at using the iterator to actually remove the item.
import java.util.Iterator;
static main(args) { def list1 = [1, 2, 3, 4]
Iterator i = list1.iterator();
while (i.hasNext()) {
n = i.next();
if (n == 2) i.remove();
}
println(list1)
}​
but I don't see why you'd want to do it that way.

How can I compare two lists in Groovy

How can I compare the items in two lists and create a new list with the difference in Groovy?
I'd just use the arithmetic operators, I think it's much more obvious what's going on:
def a = ["foo", "bar", "baz", "baz"]
def b = ["foo", "qux"]
assert ["bar", "baz", "baz", "qux"] == ((a - b) + (b - a))
Collections intersect might help you with that even if it is a little tricky to reverse it. Maybe something like this:
def collection1 = ["test", "a"]
def collection2 = ["test", "b"]
def commons = collection1.intersect(collection2)
def difference = collection1.plus(collection2)
difference.removeAll(commons)
assert ["a", "b"] == difference
I assume the OP is asking for the exclusive disjunction between two lists.
(Note: Neither of the previous solutions handle duplicates!)
If you want to code it yourself in Groovy, do the following:
def a = ['a','b','c','c','c'] // diff is [b, c, c]
def b = ['a','d','c'] // diff is [d]
// for quick comparison
assert (a.sort() == b.sort()) == false
// to get the differences, remove the intersection from both
a.intersect(b).each{a.remove(it);b.remove(it)}
assert a == ['b','c','c']
assert b == ['d']
assert (a + b) == ['b','c','c','d'] // all diffs
One gotcha when using lists/arrays of ints. You may have problems due to the polymorphic method remove(int) vs remove(Object). See here for a (untested) solution.
Rather than reinventing the wheel, you should just use a library (e.g. commons-collections):
#Grab('commons-collections:commons-collections:3.2.1')
import static org.apache.commons.collections.CollectionUtils.*
def a = ['a','b','c','c','c'] // diff is [b, c, c]
def b = ['a','d','c'] // diff is [d]
assert disjunction(a, b) == ['b', 'c', 'c', 'd']
If it is a list of numbers, you can do this:
def before = [0, 0, 1, 0]
def after = [0, 1, 1, 0]
def difference =[]
for (def i=0; i<4; i++){
difference<<after[i]-before[i]
}
println difference //[0, 1, 0, 0]

Resources