Collect only if result is not null - groovy

I have a collection and I'm wanting to find certain elements and transform them. I can do this in two closures but I was wondering if it is possible with only one?
def c = [1, 2, 3, 4]
def result = c.findAll {
it % 2 == 0
}
result = result.collect {
it /= 2
}
My true use case is with Gradle, I want to find a specific bunch of files and transform them to their fully-qualified package name.

You can use findResults:
def c = [1, 2, 3, 4]
c.findResults { i ->
i % 2 == 0 ? // if this is true
i / 2 : // return this
null // otherwise skip this one
}
Also, you will get [] in case none of the elements satisfies the criteria (closure)

Related

Pick elements on (un)even index from an array in Groovy

I have a Groovy array containing digits of a number. I need to create two new arrays containing only the digits at even resp. uneven positions from that array.
The best way that I could find is this, but I feel there's quite a lot of room for improvement here:
def evenDigits = digits
.indexed(1)
.findAll { i, v -> i % 2 == 0 }
.collect { it.value }
Obviously the unevenDigits variant would be to simply check the modulus in the findAll closure against 1 instead of 0.
Does anyone know if this code can be improved or compacted?
A "less smarter" (and definitely more performant) solution:
def evens = [], odds = []
digits.eachWithIndex{ v, ix -> ( ix & 1 ? odds : evens ) << v }
You can use groupBy to separate the results to odd/even items. E.g.
groovy:000> ["a","b","c"].indexed(1).groupBy{ i, _ -> i & 1 }.collectEntries{ k, v -> [k as Boolean, v.values()] }
===> [true:[a, c], false:[b]]
One more "Groovy" solution that uses withIndex() and findResults() combination.
withIndex() transforms a List<T> to List<Tuple2<T,Integer>> - a list of value-index tuples.
findResults(closure) runs filtering transformation - the closure it receives is a transforming predicate. In our case, it checks if the index value is odd or even and extracts the value from tuple if the predicate matches. (All null values are filtered out.)
Short and concise. Requires a minimal number of transformations: List<T> to List<Tuple2<T,Integer>> and then a single iteration to produce the final result.
def numbers = [1,2,3,4,5,6,2,3,1] // Some test data
def even = { t -> t.second % 2 == 0 ? t.first : null } // "Even" transforming predicate
def odd = { t -> t.second % 2 == 1 ? t.first : null } // "Odd" transforming predicate
def evens = numbers.withIndex(1).findResults even
def odds = numbers.withIndex(1).findResults odd
// And some assertions to test the implementation
assert evens == [2,4,6,3]
assert odds == [1,3,5,2,1]
Another option, for a single pass (but still with the intermediate collection due to indexed), would be a reduce:
def (odd,even) = digits.indexed().inject([[],[]]){ acc, it -> acc[it.key&1] << it.value; acc }
I came up with this, but it's probably not the cleverest way.
def isEven = { int x -> x % 2 == 0 ? x : null}
def (digits, evens, odds) = [[1, 2, 3, 4, 5, 6, 7, 8, 9], [], []]
digits.each {
if (isEven(it))
evens.add(isEven(it))
}
odds = digits - evens
assert evens == [2, 4, 6, 8]
assert odds == [1, 3, 5, 7, 9]

This for circle cannot delete every same elements in a list, I don't know why [duplicate]

This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 4 years ago.
a = [1, 2, 2]
value = 2
for i in a:
if i == value:
a.remove(i)
I want to delete 2 same elements in a list. But the result tells me I just delete one of them. when I debug it, I find it only cycle 2 times, not 3 as I wish.
You can use a simple list comprehension for a 1-line solution:
In [2764]: a = [1, 2, 2]
In [2755]: value = 2
In [2768]: a = [i for i in a if i != value]
In [2769]: a
Out[2769]: [1]
You can write the above as :
ans = []
for i in a:
if i <> value:
ans.append(i)
OR, you can also use filter to remove all occurrences:
Python-2
In [2778]: filter(lambda x: x!= value, a)
Out[2778]: [1]
Python-3
In [5]: list(filter(lambda x: x!= value, a))
Out[5]: [1]
Here you don't have to use a comparison to remove a certain value from list
Here is a little modification to your code:
a = [1, 2, 2]
value = 2
try:
for i in a: a.remove (value)
except ValueError: pass
print (a)
Whenever the remove () function couldn't find the value you are looking for, it will raise a value error: ValueError: list.remove(x): x not in list. To eliminate this, surround it with a try except. That'll do the trick.
However there are more easier methods to use remove () function. For an example you could use while loop.
Look at this code:
a = [1, 2, 2]
value = 2
while value in a:
a.remove (value)
print (a)
Its far more easier.

What is the difference between any and find in groovy?

I am trying to get the set values from closure in groovy:
myList(1, 2, 3).any { it > 2 }
myList(1, 2, 3).find { it > 2 }
So not able to figure out, which one to use and better.
any returns boolean - true if any of the elements on the list matches the closure condition, while find returns first element that meets the criteria in closure being passed.
If you need to know if there're elements matching certain criteria, use any, if you need only a single element (the first one) use, find, if you need all the elements that matches the closure passed use findAll.
Example:
assert [1, 2, 3].any { it > 1 }
assert [1, 2, 3].find { it > 1 } == 2
assert [1, 2, 3].findAll { it > 1 } == [2, 3]

What's the difference between 1..5, [*1..5] and [1..5] in Groovy?

In Groovy,what is the difference between,
def a=1..5
def b= [*1..5]
def c=[1..5]
what does * in [*1..5] symbolize?
* represents a Spread Operator. Elaborating your example:
a = 1..5
b = [*1..5]
c = [1..5]
assert a.class.name == "groovy.lang.IntRange" //Is a range from 1 till 5
assert b.class.name == "java.util.ArrayList" //Spread the range in a list
assert c.class.name == "java.util.ArrayList" //Is a list
Extending #ataylor's explanation:
assert a.size() == 5
assert b.size() == 5
assert c.size() == 1
To reach each element in c you have to iterate over it (which is a range)
c.each{println it}
Groovy Goodness by Mr Haki has a detailed example of its usage.
When you put a range object in a list, you get a list with one element of type IntRange:
assert [1..5].size() == 1
By applying the spread operator it expands the range and you get a list with five elements, the actual integers the range represents:
assert [*1..5].size() == 5
Here * (spread operator) expands the range 1..5 and hence you get a list of integers in that range [1, 2, 3, 4, 5]

How to get all elements from a list without the last element?

The following Groovy code
lines = ['0','1','2','3','4','5']
println lines[1..lines.size()-1]
println lines[1..-1]
println lines[1..<lines.size()-1]
println lines[1..<-1]
println lines[1..<-2]
println lines[1..-2]
produces this output:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
[1, 0]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
Since -1 is the index of the last element in the list, the first two make sense (ranges in Groovy include the end element instead of omitting it as everywhere else in Java :-( )
Line #3 is the desired output (list without first and last element).
I'm worried about the output #4: Why do I get [1, 0] for 1..-1?
Also [1, 2, 3, 4, 5] for the range 1..<-2 seems wrong.
Why does that happen?
The best way to take all elements but the last one, in my opinion, is to use the take method:
def list = ['a', 'b', 'c']
assert list.take(list.size() - 1) == ['a', 'b']
It behaves properly in the corner case where size == 1:
def list = ['one']
assert list.take(list.size() - 1) == []
Though I'd prefer it to throw an exception in the case size == 0, but the behavior is not that bad:
def list = []
assert list.take(list.size() - 1) == []
You can also use list[0..<list.size()-1] (your third example) and it will behave the same except for the empty list, in which case it will throw an ArrayIndexOutOfBoundsException, but i think is not as readable as the take counterpart.
Another acceptable solution is using list[0..-2] (your last example), which i think looks much more elegant, but unfortunately breaks when size == 1 with an ArrayIndexOutOfBoundsException.
In your examples (i'll assume that you meant to use 0 as the starting index instead of 1 if you wanted to include all elements but the last one):
lines[0..lines.size()-1] is equivalent to lines[0..-1] because the getAt(Range) method of lists will treat ranges with negative indexes the same way asgetAt(Integer) does, i.e. as accessing the (list.size() + negativeIndex)'th element of the list. Therefore list[0..-1] is the same as saying "from first element to last" and it's the same as copying the list; and list[-1..0] is the same as "from last to first" and it's equivalent to list.reverse() :)
The problem with the other non-inclusive range example is that the non-inclusive ranges are being evaluated before the list access and they evaluate to an incorrect inclusive range. For example, 0..<-2 evaluates to 0..-1, and that's why it's returning all the elements. 0..<-1 evaluates to 0..0 and it returns only the first element.
Notice that the empty range is a special case. It's denoted as 0..<0 and it doesn't have an inclusive equivalent (so Groovy won't do any magic conversion here). And that's why list[0..<list.size()-1] works when size == 1 (the range evaluates to the empty range) while list[0..-2] doesn't :)
Maybe this has changed since epideman wrote his answer, but you can get the whole list without the last element with 0..<-1:
assert ["foo"][0..<-1] == []
assert ["foo", "bar"][0..<-1] == ["foo"]
assert ["foo", "bar", "baz"][0..<-1] == ["foo", "bar"]
// blows up if empty, here take is better
assert [][0..<-1] == [] // BOOM
// if you want null safe, use take
assert [].take(-1) == []
This is with groovy 2.2.1.
Since Groovy 2.4 you can use the init() method:
lines = ['0','1','2','3','4','5']
assert lines.init() == ['0','1','2','3','4']

Resources