Groovy String concatenation with null checks - string

Is there a better way to do this? Note: part1, part2 and part3 are string variables defined elsewhere (they can be null).
def list = [part1, part2, part3]
list.removeAll([null])
def ans = list.join()
The desired result is a concatenated string with null values left out.

You can do this:
def ans = [part1, part2, part3].findAll({it != null}).join()
You might be able to shrink the closure down to just {it} depending on how your list items will evaluate according to Groovy Truth, but this should make it a bit tighter.
Note: The GDK javadocs are a great resource.

If you use findAll with no parameters. It will return every "truthful" value, so this should work:
def ans = [part1, part2, part3].findAll().join()
Notice that findAll will filter out empty strings (because they are evaluated as false in a boolean context), but that doesn't matter in this case, as the empty strings don't add anything to join() :)
If this is a simplified question and you want to keep empty string values, you can use findResults{ it }.

Alternatively, you can do this as a fold operation with inject:
def ans = [part1, part2, part3].inject('') { result, element ->
result + (element ?: '')
}
This iterates the whole list and concatenates each successive element to a result, with logic to use the empty string for null elements.

You could use grep:
groovy:000> list = ['a', 'b', null, 'c']
===> [a, b, null, c]
groovy:000> list.grep {it != null}.join()
===> abc

Related

how to add characters from array into one string python

I'm trying to change characters from x into upper or lower character depending whether they are in r or c. And the problem is that i can't get all the changed characters into one string.
import unittest
def fun_exercise_6(x):
y = []
r = 'abcdefghijkl'
c = 'mnopqrstuvwxz'
for i in range(len(x)):
if(x[i] in r):
y += x[i].lower()
elif(x[i] in c):
y += x[i].upper()
return y
class TestAssignment1(unittest.TestCase):
def test1_exercise_6(self):
self.assertTrue(fun_exercise_6("osso") == "OSSO")
def test2_exercise_6(self):
self.assertTrue(fun_exercise_6("goat") == "gOaT")
def test3_exercise_6(self):
self.assertTrue(fun_exercise_6("bag") == "bag")
def test4_exercise_6(self):
self.assertTrue(fun_exercise_6("boat") == "bOaT" )
if __name__ == '__main__':
unittest.main()
Using a list as you are using is probably the best approach while you are figuring out whether or not each character should be uppered or lowered. You can join your list using str's join method. In your case, you could have your return statement look like this:
return ''.join(y)
What this would do is join a collection of strings (your individual characters into one new string using the string you join on ('').
For example, ''.join(['a', 'b', 'c']) will turn into 'abc'
This is a much better solution than making y a string as strings are immutable data types. If you make y a string when you are constructing it, you would have to redefine and reallocate the ENTIRE string each time you appended a character. Using a list, as you are doing, and joining it at the end would allow you to accumulate the characters and then join them all at once, which is comparatively very efficient.
If you define y as an empty string y = "" instead of an empty list you will get y as one string. Since when you declare y = [] and add an item to the list, you add a string to a list of string not a character to a string.
You can't compare a list and a string.
"abc" == ["a", "b", "c'] # False
The initial value of y in the fun_exercise_6 function must be ""

Create Map with CollectEntries in Groovy

I have the following list:
appList = [DevOpsApplication, 01.01.01]
I would like to create a map using collectEntries. I know that it refers to the current element of an iteration (shortcut for { it -> it }). Therefore, I tried to use the index:
def appMap = appList.collectEntries { [(it[0]):it[1]] }
However, this gives me:
[D:e, 0:1]
But I want [DevOpsApplication: 01.01.01]. Is there a way to do this?
Additionally, In future I would like this to expand to more than 2 elements (e.g. [DevOpsApplication, 01.01.01, AnotherDevOpsApplication, 02.02.02]) with the desired output of [DevOpsApplication: 01.01.01, AnotherDevOpsApplication: 02.02.02].
How will this be possible?
A very short version to do this would be:
def appList = ["DevOpsApplication", "01.01.01"]
def appMap = [appList].collectEntries() // XXX
assert appMap == [DevOpsApplication: "01.01.01"]
How does it work: the function collectEntries takes, is expected to return a map or a two element list. Your appList is already that. So put that in another list, call collectEntries on it. When no function is given to collectEntries it uses the identity function.
Bonus: what if appList has much more elements? You can use collate to build the tuples.
def appList = ["DevOpsApplication", "01.01.01", "Some", "More"]
def appMap = appList.collate(2).collectEntries() // XXX
assert appMap == [DevOpsApplication: "01.01.01", Some: "More"]
I also found another method. Groovy can convert the values of an Object array and convert them into a map with the toSpreadMap(). However, the array must have an even number of elements.
def appList = ['DevOpsApplication', '01.01.01']
def appMap = appList.toSpreadMap()
You're iterating element-by-element and (because your elements are String-typed) mapping 0 substrings to 1 substrings.
You can use this to skip one element in each iteration and map each element at even indices to the one after it:
def appList = ['DevOpsApplication', '01.01.01']
def appMap = (0..(appList.size()-1)).findAll{0 == it%2}
.collectEntries{[(appList[it]): appList[it+1]]}
That returns [DevOpsApplication:01.01.01] as expected.

Return multiple values from map in Groovy?

Let's say I have a map like this:
def map = [name: 'mrhaki', country: 'The Netherlands', blog: true, languages: ['Groovy', 'Java']]
Now I can return "submap" with only "name" and "blog" like this:
def keys = ['name', 'blog']
map.subMap(keys)
// Will return a map with entries name=mrhaki and blog=true
But is there a way to easily return multiple values instead of a list of entries?
Update:
I'd like to do something like this (which doesn't work):
def values = map.{'name','blog'}
which would yield for example values = ['mrhaki', true] (a list or tuple or some other datastructure).
map.subMap(keys)*.value
The Spread Operator (*.) is used to invoke an action on all items of
an aggregate object. It is equivalent to calling the action on each
item and collecting the result into a list
You can iterate over the submap and collect the values:
def values = map.subMap(keys).collect {it.value}
// Result: [mrhaki, true]
Or, iterate over the list of keys, returning the map value for that key:
def values = keys.collect {map[it]}
I would guess the latter is more efficient, not having to create the submap.
A more long-winded way to iterate over the map
def values = map.inject([]) {values, key, value ->
if (keys.contains(key)) {values << value}
values
}
For completeness I'll add another way of accomplishing this using Map.findResults:
map.findResults { k, v -> k in keys ? v : null }
flexible, but more long-winded than some of the previous answers.

Groovy remove duplicate from list using unique function

I'm using soapui groovy script.
I want to remove the duplicate from a list, using the next code:
def myStr = "aaaa ; bbbb ; ccccc"
def myList = myStr.split(";")
myList = myList.unique()
but when i tun the code i get exception:
No signature of method: [Ljava.lang.String;.unique() is applicable for
argument types: () values: [] Possible solutions:
minus(java.lang.Object), minus(java.lang.Iterable),
minus([Ljava.lang.Object;), size(), use([Ljava.lang.Object;),
use(java.lang.Class, groovy.lang.Closure)
Use tokenize() instead of split() which returns an ArrayList as compared to split which return a String Array.
def myStr = "aaaa ; bbbb ; ccccc;"
def myList = myStr.tokenize(";")*.trim()
myList = myList.unique()
or use toList() if you are using split() or cast the String array to a Set.
However, based on the question you want to remove the duplicate items from list but I do not see any duplicate item. If you mean to remove duplicate strings from the list items then use:
myList = myList.unique().collect { it.toSet().join() }
Simple, .split() returns an array, you just need to convert it to a (Groovy) List. Any of the following will make the unique() method work.
def myList = myStr.split(";").collect()
or
def myList = (List)myStr.split(";")
or
def myList = myStr.split(";").toList()
If you cast it to a java.util.Set, it'll only keep unique values!
def myList = (Set)myStr.split(";")
Gotcha: Be careful though, the strings still contain the spaces!

Groovy: same parameters, different results

In the below code, x.test() returns [1,2].
So y = [1,2].
Yet f([1,2]) prints 1, but f(y) prints 2.
How do I write f(y) so it prints 1?
Perversely, f(z) prints 1, even though z = y.
def f = { Object... args -> println args.size(); };
class Test { Object[] test() { return [1,2]; } }
def x = new Test();
def y = x.test();
def z = [1,2];
f([1,2]); // 1
f(y); // 2
f(z); // 1
The problem is that y and z, while they appear to be the same, are actually of different types. y is an Object[] while z is an ArrayList<Integer>. Groovy handles arrays and lists differently, automatically coercing the former into a varargs parameter list, but not the latter.
println y.getClass(); // class [Ljava.lang.Object
println z.getClass(); // class java.util.ArrayList
As for a solution to your problem, either change your test() to return a List instead of an array:
class Test { List test() { return [1,2]; } }
or manually coerce the array into a list when you pass it to f:
f(y as List); // 1
The expression [1,2] in Groovy denotes an ArrayList with two members, Integer.valueOf(1) and Integer.valueOf(2). Thus when you call f([1,2]) Groovy creates a single-element array containing this ArrayList as its only item, and passes that array as the closure argument.
But x.test() is declared to return Object[] so the [1,2] ArrayList will be converted to a two element Object[] by the return. Thus y is already an Object[] and does not need to be boxed up in a varargs array to be passed to f.
You need to turn y back into a list, either by changing the return type of test() or by saying
f(y as List)
Conversely, you can use the spread operator
f(*z) // 2
which will extract the elements of the ArrayList and pass them as individual arguments to the call (which will then be packaged up into a varargs array as usual).
y is an instance of [Ljava.lang.Object whereas [1,2] and z are of instance of ArrayList
The Array has the size() == 2 and the ArrayLists count as one argument, but they contain two elements
Groovy does some type converting for you ;)

Resources