groovy map populate with default element - groovy

Is there more Groovish way of adding an element to map of lists and initialize default list if not exists?
Or in other words what would be a Groovish way to code the below:
def mylist = [1,2,3,4]
def mymap = [:]
for (num in mylist){
if (num % 2 == 0){
pairity = "even"
} else {
pairity = "odd"
}
if (mymap.containsKey(pairity)){
println("Adding to Even")
mymap[pairity].add(num)
}
else {
println("adding to Odd")
mymap[pairity] = [num]
}
}
print(mymap.toString())
// adding to Odd
// adding to Odd
// Adding to Even
// Adding to Even
// [odd:[1, 3], even:[2, 4]]

You can use withDefault on a map to have automatically generate a value for a missing key on access.
[1,2,3,4].inject([:].withDefault{[]}){ m, i -> m[ i%2==0 ? 'odd' : 'even' ] << i; m }
// => [even:[1, 3], odd:[2, 4]]

You can simply groupby:
def mymap = mylist.groupBy { it % 2 == 0 ? 'even' : 'odd' }
That is effectively using the closure to partition the list on the condition.

Related

Groovy: Get index of all occurences of sublist from arraylist

I am new to groovy and trying to find the indexes of all sublists in a list.
I am trying to use something like Collections.indexOfSubList like in java but it gives exception saying it applies on Lists and not ArrayLists.
So I am trying to define my own function. I am finding all the indices of all the elements in the smaller list existing in the longer list and then subtracting the indices of the result array. If it comes to 1 then I am considering that index to a sublist.
I know that I have the logic a little twisted. Can somebody guide with a better and efficient way of doing this.
Below is my code:
List list1 = [1,2,3,4,5,6,1,2,3]
List list2 = [1,2]
index1 = list1.findIndexValues {
it == list2[0];
}
index2 = list1.findIndexValues {
it == list2[1];
}
println index1
println index2
result = []
for (int i = 0; i < index1.size(); i++) {
result.add(index2[i]-index1[i]);
}
println result
Edit: no longer uses Collections due to new issue re: Elastic Search.
The following code traverses along the source list, creating a sublist. It checks the sublist to see if it starts with the target list. See the asserts below (e.g. the indexes are 0-based):
def listStartsWithSubList = { source, target ->
def result = false
if (source.size() >= target.size()) {
result = true
target.eachWithIndex { item, index ->
result = result && (item == source[index])
}
}
result
}
def indexOfSubLists = { source, target ->
def results = []
source.eachWithIndex { item, index ->
def tmpList = source[index..source.size()-1]
if (listStartsWithSubList(tmpList, target)) {
results << index
}
}
results
}
assert [1] == indexOfSubLists([1,2,3], [2,3])
assert [2] == indexOfSubLists([1,2,3], [3])
assert [] == indexOfSubLists([1,2,3], [4])
assert [0,6] == indexOfSubLists([1,2,3,4,5,6,1,2,3], [1,2])

I need a shorthand way to combine Maps in groovy with slightly different results than +

NOTE: This can be done as a method call or an operator override pretty easily, I am looking for an intrinsic one-line solution that I don't have to carry around in a library.
When you combine(add) Maps, you get a result like this:
println [a:1,c:3] + [a:2]
// prints {a=2, c=3}
I seem to keep needing results more like:
{a=[1, 2], c=[3]}
In other words, something that combines all the values from identical keys in the Maps.
Is there an operator or simple function call that does this, because doing it myself always seems to break my stride a little. It seems like the * operator might do this nicely, but it doesn't.
Is there an easy way to do this?
Another alternative (adding it to the * operator on Maps)
def a = [ a:1, c:10 ]
def b = [ b:1, a:3 ]
Map.metaClass.multiply = { Map other ->
(delegate.keySet() + other.keySet()).inject( [:].withDefault { [] } ) { m, v ->
if (delegate[v] != null) { m[v] << delegate[v] }
if (other[v] != null) { m[v] << other[v] }
m
}
}
assert a * b == [a:[1, 3], c:[10], b:[1]]
Came up with this as well, but it's late and there are probably better, shorter ways
def a = [ a:1, c:10 ]
def b = [ b:1, a:3 ]
[a,b]*.collect {k,v -> [(k):v]}
.flatten()
.groupBy { it.keySet()[0]}
.inject([:].withDefault{[]}) {m,v->
m << [(v.key):v.value[v.key]]
}
​​
Nothing came to my mind, so I start the bidding with this:
m1 = [a:1, c:666]; m2 = [a:2, b:42]
result = [:].withDefault{[]}
[m1,m2].each{ it.each{ result[it.key] << it.value } }
assert result == [a:[1,2], b:[42], c:[666]]

What is the neatest way to remove all but the last item from a list in groovy?

I've been trying to find a neat way to remove all but the last element from a list in groovy, but all the things I've tried seem a bit overcomplicated. Is there a neater way?
FAILS: java.util.ConcurrentModificationException
void removeAllButLastInPlace(list) {
if(list.size() > 1) {
def remove = list[0..-2]
remove.each { list.remove(it) }
}
}
FAILS: java.lang.CloneNotSupportedException: java.util.ArrayList$SubList
void removeAllButLastInPlace(list) {
if(list.size() > 1) {
def remove = list[0..-2].clone()
remove.each { list.remove(it) }
}
}
WORKS, but list construction seems unnecessary
void removeAllButLastInPlace(list) {
if(list.size() > 1) {
def remove = [] + list[0..-2]
remove.each { list.remove(it) }
}
}
WORKS, but seems a bit arcane
void removeAllButLastInPlace(list) {
(list.size() - 1).times { list.remove(0) }
}
WORKS, perhaps most 'correct'
void removeAllButLastInPlace(list) {
list.retainAll { list.lastIndexOf(it) == list.size() - 1 }
}
The code should fulfil the following tests:
list = []
removeAllButLastInPlace(list)
assert list == []
list = ['a']
removeAllButLastInPlace(list)
assert list == ['a']
list = ['a', 'b']
removeAllButLastInPlace(list)
assert list == ['b']
list = ['a', 'b', 'c']
removeAllButLastInPlace(list)
assert list == ['c']
Rather than mutating an existing list., why not return a new list?
Then you can simply do:
List removeAllButLast( List list ) {
list ? [list[-1]] : []
}
Or:
List removeAllButLastInPlace( List list ) {
list.drop( list.size() - 1 )
}
edit:
You could also use a loop (if you have to have a mutating method)
void removeAllButLastInPlace( List list ) {
while( list.size() > 1 ) list.remove( 0 )
}
void removeAllButLastInPlace( List list ) {
def index = 0
list.reverse(true).retainAll({index++ == 0})
}
I'd go for one of these. My criteria being a) modifying the list in place as stated in your requirement (not returning a new list) and b) succinctness.
I have however made both versions return a reference to the modified list just to simplify the println examples. You could make the return type 'void' if preferred and remove the last 'xs' line from each.
def xs1 = [1, 2, 3, 4, 5]
def xs2 = [9]
def xs3 = []
def keepOnlyLast1(xs) {
while(xs.size() > 1) xs.remove(0)
xs
}
def keepOnlyLast2(xs) {
xs[0 ..< xs.size()-1] = []
xs
}
println "Version 1"
println keepOnlyLast1([] + xs1)
println keepOnlyLast1([] + xs2)
println keepOnlyLast1([] + xs3)
println "Version 2"
println keepOnlyLast2([] + xs1)
println keepOnlyLast2([] + xs2)
println keepOnlyLast2([] + xs3)
/* Sanity check that these methods *really* modify the list. */
println "Sanity Check"
keepOnlyLast1(xs1)
println xs1
keepOnlyLast2(xs2)
println xs2
Aside: I would also go for a more functional style if at all possible (i.e. return a new list rather than modifying the existing one) except that that wasn't what you asked for. Here's an example anyway:
def lastAsList(xs) {
xs.isEmpty() ? [] : [xs[-1]]
}
println lastAsList([1, 2, 3])
println lastAsList([])

Remove numbers which are repeated several times in a row

I have collection
def list = [4,1,1,1,3,5,1,1]
and I need to remove numbers which are repeated three times in a row. As a result I have to get an [4,3,5,1,1]. How to do this in groovy ?
This can be done by copying the list while ensuring the two previous elements are not the same as the one to be copied. If they are, drop the two previous elements, otherwise copy as normal.
This can be implemented with inject like this:
def list = [4,1,1,1,3,5,1,1]
def result = list.drop(2).inject(list.take(2)) { result, element ->
def prefixSize = result.size() - 2
if ([element] * 2 == result.drop(prefixSize)) {
result.take(prefixSize)
} else {
result + element
}
}
assert result == [4,3,5,1,1]
You can calculate the uniques size in the next three elements and drop them when they are 1:
def list = [4,1,1,1,3,5,1,1]
assert removeTriplets(list) == [4,3,5,1,1]
def removeTriplets(list) {
listCopy = [] + list
(list.size()-3).times { index ->
uniques = list[index..(index+2)].unique false
if (uniques.size() == 1)
listCopy = listCopy[0..(index-1)] + listCopy[(index+3)..-1]
}
listCopy
}
Another option is to use Run Length Encoding
First lets define a class which will hold our object and the number of times it occurs in a row:
class RleUnit {
def object
int runLength
RleUnit( object ) {
this( object, 1 )
}
RleUnit( object, int runLength ) {
this.object = object
this.runLength = runLength
}
RleUnit inc() {
new RleUnit( object, runLength + 1 )
}
String toString() { "$object($runLength)" }
}
We can then define a method which will encode a List into a List of RleUnit objects:
List<RleUnit> rleEncode( List list ) {
list.inject( [] ) { r, v ->
if( r && r[ -1 ].object == v ) {
r.take( r.size() - 1 ) << r[ -1 ].inc()
}
else {
r << new RleUnit( v )
}
}
}
And a method that takes a List of RleUnit objects, and unpacks it back to the original list:
List rleDecode( List<RleUnit> rle ) {
rle.inject( [] ) { r, v ->
r.addAll( [ v.object ] * v.runLength )
r
}
}
We can then encode the original list:
def list = [ 4, 1, 1, 1, 3, 5, 1, 1 ]
rle = rleEncode( list )
And filter this RleUnit list with the Groovy find method:
// remove all elements with a runLength of 3
noThrees = rle.findAll { it.runLength != 3 }
unpackNoThrees = rleDecode( noThrees )
assert unpackNoThrees == [ 4, 3, 5, 1, 1 ]
// remove all elements with a runLength of less than 3
threeOrMore = rle.findAll { it.runLength >= 3 }
unpackThreeOrMore = rleDecode( threeOrMore )
assert unpackThreeOrMore == [ 1, 1, 1 ]

In Groovy, how do I add up the values for a certain property in a map?

I have the following map:
def map = [];
map.add([ item: "Shampoo", count: 5 ])
map.add([ item: "Soap", count: 3 ])
I would like to get the sum of all the count properties in the map. In C# using LINQ, it would be something like:
map.Sum(x => x.count)
How do I do the same in Groovy?
Assuming you have a list like so:
List list = [ [item: "foo", count: 5],
[item: "bar", count: 3] ]
Then there are multiple ways of doing it. The most readable is probably
int a = list.count.sum()
Or you could use the Closure form of sum on the whole list
int b = list.sum { it.count }
Or you could even use a more complex route such as inject
int c = list.count.inject { tot, ele -> tot + ele } // Groovy 2.0
// c = list.count.inject( 0 ) { tot, ele -> tot + ele } // Groovy < 2.0
All of these give the same result.
assert ( a == b ) && ( b == c ) && ( c == 8 )
I would use the first one.
You want to use the collect operator. I checked the following code with groovysh:
list1 = []
total = 0
list1[0] = [item: "foo", count: 5]
list1[1] = [item: "bar", count: 3]
list1.collect{ total += it.count }
println "total = ${total}"
First of all, you're confusing map and list syntax in your example. Anyhow, Groovy injects a .sum(closure) method to all collections.
Example:
[[a:1,b:2], [a:5,b:4]].sum { it.a }
===> 6

Resources