In Groovy, can you use Range as a List? - groovy

According to the Groovy docs:
Ranges allow you to create a list of sequential values. These can be used as Lists since Range extends java.util.List.
However, in my case I need the List to end up as a String, including the square brackets. I tried the following:
def myRange = 1..5
def myList = [1, 2, 3, 4, 5]
// this passes
assert myRange == myList
// both of the following fail!
assert myRange.toString() == myList.toString()
assert myRange.subList(0, 5).toString() == myList.toString()
Am I missing something?

Extending something does not mean that its toString will be the same.
If you have to get the same output as with a list, try
myRange.toList().toString()
Or
"[${myRange.join(',')}]"
Or (adding comment as an easy answer for you)
assert (1..5).toListString() == "[1, 2, 3, 4, 5]"

Related

What is the empty dictionary used for in the code?

I'm doing practice problems in python on Leetcode (still learning). This is the problem:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
my code is
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
dict = {}
for counter, i in enumerate(nums):
a = target- i
if a in dict:
return (dict[a], counter)
dict[i] = counter
It runs fine and passes all the tests however I found a common reason this works is for the dict = {}
What is the reason for this dictionary and how does this code recognize cases for (3,3) target = 6 where there are duplicates and index matters. A basic run down of why the code works would be great!
The dictionary stores as keys the numbers in the list with their index as a value.
For example:
[2, 7, 11, 15] -> {'2':0, '7':1, '11':2, '15':3}
There is never a duplicate inserted, if the same number appears twice, the index will be replaced with the new index where it appears.
In the case of duplicate, it is important to test all value on the first list, and to store index on a separated dict in order to be sur that you will never test in dictionnary the actually tested value.
By using a dictionnary in order to find the index of the right number, you can't store duplicate.
Since in dictionnary you can't have 2 values with the same key, if duplicate, you just change the old index with the new one.
For example, if dict == {'3': 0, '2':1} and the tested value is 2, the dict == {'3': 0, '2':2}.
And if the target is reach by duplicate number (2+2 for target 4 for example), nothing is stored cause of the return in the if a in dict: return (dict[a], counter)

Python change first with last element

I want to change the first element of a string with the last element.
def change(string):
for i in range(16):
helper = string[i]
string[i]=string[15-i]
string[15-i]=helper
return string
print (change("abcdefghijklmnop"))
Error Output:
string[i]=helper2[0]
TypeError: 'str' object does not support item assignment
You can't alter a string; they're immutable. You can create a new string that is altered as you want:
def change(string):
return string[-1]+string[1:-1]+string[0]
You can use "*" operator.
my_list = [1,2,3,4,5,6,7,8,9]
a, *middle, b = my_list
my_new_list = [b, *middle, a]
my_list
[1, 2, 3, 4, 5, 6, 7, 8, 9]
my_new_list
[9, 2, 3, 4, 5, 6, 7, 8, 1]
Read here for more information.
As you discovered, strings are immutable so you can index a string (eg string[x]), but you can't assign to an index (eg string[x] = 'z').
If you want to swap the first and last element, you will need to create a new string. For example:
def change(input_str):
return input_str[-1] + input_str[1:-1] + input_str[0]
However, based on your example code, it looks like you trying to swap all the "elements" of the string. If you want to do that, see this previous discussion on different methods of reversing a string: Reverse a string in Python
Additionally, even if you could "index" a string, your code would not work as written. With a minor change to "explode" it into a list:
def change(string):
string = [c for c in string]
for i in range(16):
helper = string[i]
string[i]=string[15-i]
string[15-i]=helper
return string
print (change("abcdefghijklmnop"))
DEMO
As you can see, the output is the "same" as the input (except exploded into a list) because you step through every index in the string, reverse all of them twice (which puts them back in their original position).

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

How to Add item to start of list using Groovy?

How can I use Groovy to add an item to the beginning of a list?
list.add(0, myObject);
You can also read this for some other valuable examples:
http://groovy.codehaus.org/JN1015-Collections
Another option would be using the spread operator * which expands a list into its elements:
def list = [2, 3]
def element = 1
assert [element, *list] == [1, 2, 3]
Another alternative would be to put the element into a list and concatenate the two lists:
assert [element] + list == [1, 2, 3]​
Caution!
From Groovy 2.5:
list.push( myObject )
Prior to Groovy 2.5 list.push appends ... but from 2.5/2.6 (not yet Beta) it will (it seems) prepend, "to align with Java"... indeed, java.util.Stack.push has always prepended.
In fact this push method does not belong to List, but to GDK 2.5 DefaultGroovyMethods, signature <T> public static boolean push(List<T> self, T value). However, because of Groovy syntax magic we shall write as above: list.push( myObject ).
def list = [4, 3, 2, 1, 0]
list.plus(0, 5)
assert list == [5, 4, 3, 2, 1, 0]
You can find more examples at this link

Resources