I was trying to pick a random element from a list of int and the list elements are created with range operator:
def date_range = 1..28
assert ('a'..'d').collect() == ['a','b','c','d'] // So this is instanceof List
assert ('a'..'d') == ['a','b','c','d'] // So this is instanceof List
def x1 = (1..28).collect().sort{new Random()}?.take(1)[0] // works
def x2 = date_range.collect().sort{new Random()}?.take(1)[0] // works
def x3 = date_range[0..27].sort{new Random()}?.take(1)[0] //works
def x4 = date_range.sort{new Random()}?.take(1)[0] // does not works
x4 yielding below Exception
Caught: java.lang.UnsupportedOperationException
java.lang.UnsupportedOperationException
at listManipulation.run(listManipulation.groovy:21)
What is my mistake on x4?
Update:
My confusion is : if I define date_range as def date_range = 1..28 and check like: assert date_range instanceof List , than it passes. Why I should again convert it to list ?
date_range is of type IntRange. No sure really why sort is unsupported on Range. But toSorted be used to achieve the same.
You should be able to call it using below:
def date_range = 1..28
def x4 = date_range.toSorted {new Random()}?.take(1)[0]
println x4
Also, convert range to an array or list before sort(this is what happened in case of x3) like below:
println date_range.toList().sort {new Random()}?.take(1)[0]
println date_range.toArray().sort {new Random()}?.take(1)[0]
Edit: Based on OP's comments.
date_range is immutable list.
In all your x1, x2, x3, you are creating a collection / list only(over immutable list) which becomes mutable and then able to do sort. Similarly above.
sort not allowed on immutable list.
Below works.
def mutableList = ['Groovy', 'Java', 'JRuby']
mutableList.sort()
However, once the list is immutable, then it does now allow and results UnsupportedOperation Exception.
def immutableList = ['Groovy', 'Java', 'JRuby'].asImmutable()
immutableList.sort()
Hope it is clear now.
Related
This is the List that I have
List a = ["Sep->Day02->FY21;Inter01","Sep->Day02->FY21;Inter02","Sep->Day02->FY21;Inter03","Sep->Day01->FY21;Inter18","Sep->Day01->FY21;Inter19"]
I am trying to group this and generate a new string
Expected result
"Sep->Day02->FY21",Inter01:Inter03
"Sep->Day01->FY21",Inter18:Inter19
Tried to do this
List a = ["Sep->Day02->FY21;Inter01","Sep->Day02->FY21;Inter02","Sep->Day02->FY21;Inter03","Sep->Day01->FY21;Inter18","Sep->Day01->FY21;Inter19"]
List b = []
a.each{
b.add(it.split(";"))
}
def c = b.groupBy{it[0]}
println c
c.each{
k, v -> println "${v}"
}
I cant find a way to get the range of Inter01:Inter03 in the string. Please advice.
EDIT
The solution provided by Marmite works in the groovy console as expected. The list I am generating is from values in a map.
a.add(map[z]) Where z is the key.
When I am trying to use it, it gives me max and min method not found errors.
Tried using map[z].toString(). Still the same. Is the fact that the values are from a map affecting the same?
Code Snippet
Below is how I generate the map
def map = [:]
itr.each{
def Per = it.getMemberName("Period") //getMemberName is a product specific function . Sample output May
def Day = it.getMemberName("Day") //Day01 sample output
def Hour = it.getMemberName("Hour") //Interval01 sample output
def HourInt = it.getMemberName("Hour").reverse().take(2).reverse()
def Year = it.getMemberName("Years")
map.put(it.DataAsDate.format("dd/MM/yyyy")+"-"+Hour,Year+"->"+Per+"->"+Day+";"+HourInt)
}
Below is where I generate the List
def OSTinterval = OST.reverse().take(2).reverse() as Integer //This creates 01 out of Interval01
def OETinterval = OET.reverse().take(2).reverse() as Integer //This creates 03 out of Interval03
D1 = new Date(OSDDay)
D2 = new Date(OEDDay)
if (D1 == D2)
{
(OSTinterval..OETinterval).each
{inter ->
z = OSDDay+"-"+"Interval"+inter.toString().padLeft(2,'0')
coll.add(map[z].toString())
}
}
else
{
(D1..D2).each {
if (it == D1){
(OSTinterval..48).each
{inter ->
z = OSDDay+"-"+"Interval"+inter.toString().padLeft(2,'0')
coll.add(map[z].toString())
}
Basically you are looking for min and max after grouping.
I'm using a bit simplified data to focus on the processing:
def a = ['D02;I01','D02;I02','D02;I03','D01;I18','D01;I18']
println a.collect{it.split(";")}
.groupBy{it[0]}
.collect{k,v -> [k,v.collect {it[1]}]}
.collect{[it[0],"${it[1].min()}:${it[1].max()}"]}
.collect{it.join(',')}
returns a list of the keys with the min and max of the values
[D02,I01:I03, D01,I18:I18]
result after groupBy
[D02:[[D02, I01], [D02, I02], [D02, I03]], D01:[[D01, I18], [D01, I18]]]
The next collect removes the duplicated keys
[[D02, [I01, I02, I03]], [D01, [I18, I18]]]
Finally you find the min and max of the lists
[[D02, I01:I03], [D01, I18:I18]]
So I have this class:
#!/usr/bin/python3
class MyClass(object):
def __init__(self, length):
self._list = length
def get(self, index):
try:
return self._list[index]
except IndexError:
return None
which takes in a list and returns a value, a list index I think. I am trying to get that value:
def my_function(a_list):
a_list = MyClass
for x in (10**p for p in range(1, 9)):
if a_list:
print(a_list)
def main():
length = my_function(MyClass([i for i in range(0, 543)]))
but I keep getting only the memory location of the list, I think this is supposed to return an int.
I am hoping this is a workable bit of code, but I am struggling, with the concept of passing an "object" to a class, it doesn't make any sense to me.
Here is a test I am supposed to use:
def test_large_list():
s_list = My_Class([i for i in xrange(0, 100000)])
assert len(s_list._list) == list_length(s_list)
Ok, Here is my full function that works, it is done, how od I do this so that the first line takes an argument
#!/usr/bin/python3
#def list_length(single_method_list): This is what I am supposed to work with
from single_method_list import SingleMethodList
def my_function(): # This is how I have done it and it works.
a_list = MyClass([i for i in range(0, 234589)])
for x in (10**p for p in range(1, 8)):
if a_list.get(x):
print("More than", x)
first = x
else:
print("Less than", x)
last = x
break
answer = False
while not answer:
result = (first + last)/2
result = int(round(result))
print(result)
if s_list.get(result):
first = result
print('first', result)
else:
last = result
print('last', result)
if s_list.get(result) and not s_list.get(result + 1):
answer = True
print(result + 1)
my_function()
I don't know what more I can give to explain where I am stuck, it is the OOP part of this that I don't know I need the same results here, just passing it to the function instead of creating it inside the function which I did in order to do the algorithm.
Well your class does something else.MyClass is designed to take a List at initialization, so the naming length is not a good idea.
The get() method of this class takes in a number and returns the element located at that particular index in the initialized self._list.
Your logic should be like:
def my_function(a_list):
a_list = MyClass(a_list)
...
def main():
length = my_function([i for i in range(0, 543)])
Just to clarify some misunderstanding that you might have.
Class does not return anything. It is a blueprint for creating objects.
What can return value is a method (function). For instance, if you want to write a method which returns length of some list:
def my_function(some_list):
return len(some_list)
Or in your case:
def my_function(a_list):
return len(a_list._list)
Note that you should not call your variables list. It's a built-in function in python which creates lists.
And as you can see there is another built-in function len in python which returns length of list, tuple, dictionary etc.
Hope this helps, although it's still a bit unclear what you're trying to achieve.
I'm pretty new to Python and Qgis, right now I'm just running scripts but I my end-goal is to create a plugin.
Here's the part of the code I'm having problems with:
import math
layer = qgis.utils.iface.activeLayer()
iter = layer.getFeatures()
dict = {}
#iterate over features
for feature in iter:
#print feature.id()
geom = feature.geometry()
coord = geom.asPolyline()
points=geom.asPolyline()
#get Endpoints
first = points[0]
last = points[-1]
#Assemble Features
dict[feature.id() ]= [first, last]
print dict
This is my result :
{0L: [(355277,6.68901e+06), (355385,6.68906e+06)], 1L: [(355238,6.68909e+06), (355340,6.68915e+06)], 2L: [(355340,6.68915e+06), (355452,6.68921e+06)], 3L: [(355340,6.68915e+06), (355364,6.6891e+06)], 4L: [(355364,6.6891e+06), (355385,6.68906e+06)], 5L: [(355261,6.68905e+06), (355364,6.6891e+06)], 6L: [(355364,6.6891e+06), (355481,6.68916e+06)], 7L: [(355385,6.68906e+06), (355501,6.68912e+06)]}
As you can see, many of the lines have a common endpoint:(355385,6.68906e+06) is shared by 7L, 4L and 0L for example.
I would like to create a new dictionary, fetching the shared points as a key, and having the second points as value.
eg : {(355385,6.68906e+06):[(355277,6.68901e+06), (355364,6.6891e+06), (355501,6.68912e+06)]}
I have been looking though list comprehension tutorials, but without much success: most people are looking to delete the duplicates, whereas I would like use them as keys (with unique IDs). Am I correct in thinking set() would still be useful?
I would be very grateful for any help, thanks in advance.
Maybe this is what you need?
dictionary = {}
for i in dict:
for j in dict:
c = set(dict[i]).intersection(set(dict[j]))
if len(c) == 1:
# ok, so now we know, that exactly one tuple exists in both
# sets at the same time, but this one will be the key to new dictionary
# we need the second tuple from the set to become value for this new key
# so we can subtract the key-tuple from set to get the other tuple
d = set(dict[i]).difference(c)
# Now we need to get tuple back from the set
# by doing list(c) we get list
# and our tuple is the first element in the list, thus list(c)[0]
c = list(c)[0]
dictionary[c] = list(d)[0]
else: pass
This code attaches only one tuple to the key in dictionary. If you want multiple values for each key, you can modify it so that each key would have a list of values, this can be done by simply modifying:
# some_value cannot be a set, it can be obtained with c = list(c)[0]
key = some_value
dictionary.setdefault(key, [])
dictionary[key].append(value)
So, the correct answer would be:
dictionary = {}
for i in a:
for j in a:
c = set(a[i]).intersection(set(a[j]))
if len(c) == 1:
d = set(a[i]).difference(c)
c = list(c)[0]
value = list(d)[0]
if c in dictionary and value not in dictionary[c]:
dictionary[c].append(value)
elif c not in dictionary:
dictionary.setdefault(c, [])
dictionary[c].append(value)
else: pass
See this code :
dict={0L: [(355277,6.68901e+06), (355385,6.68906e+06)], 1L: [(355238,6.68909e+06), (355340,6.68915e+06)], 2L: [(355340,6.68915e+06), (355452,6.68921e+06)], 3L: [(355340,6.68915e+06), (355364,6.6891e+06)], 4L: [(355364,6.6891e+06), (355385,6.68906e+06)], 5L: [(355261,6.68905e+06), (355364,6.6891e+06)], 6L: [(355364,6.6891e+06), (355481,6.68916e+06)], 7L: [(355385,6.68906e+06), (355501,6.68912e+06)]}
dictionary = {}
list=[]
for item in dict :
list.append(dict[0])
list.append(dict[1])
b = []
[b.append(x) for c in list for x in c if x not in b]
print b # or set(b)
res={}
for elm in b :
lst=[]
for item in dict :
if dict[item][0] == elm :
lst.append(dict[item][1])
elif dict[item][1] == elm :
lst.append(dict[item][0])
res[elm]=lst
print res
I'm trying to sort a list of images names. For simplicity I have placed this in an example Groovy list of the form:
imageNames=[
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C01.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C03.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A02Z01C04.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A03Z01C02.tif'
]
I would like to be able to sort this list in the numerical order of any of the T,F,L,A,Z or C codes that are present in the suffix of the image names.
So for example, if the list was to be sorted in terms of the C code it should appear in the following order:
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C01.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A03Z01C02.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C03.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A02Z01C04.tif'
I have thought of using the default Groovy collection sort method using a comparator. However, I'm not sure how to write a comparator directly into a closure.
I would like something like
imageNames.sort{comparator_for_C}
where I can write specific comparators for each of the T,F,L,A,Z and C codes.
If the name of the image files remains the same till the 2nd _, you can skip the splitting in the logic below. I wanted to be on the safe side.
def imageNames=[
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C01.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C03.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A02Z01C04.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A03Z01C02.tif'
]
def comparator = {str->
[
compare: {a,b->
a.split(/_/)[2].dropWhile{it != str} <=>
b.split(/_/)[2].dropWhile{it != str}
}
] as Comparator
}
def comp = ['T', 'F', 'A', 'Z', 'C'].collectEntries{[it, comparator(it)]}
assert imageNames.sort(comp.'C') == [
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C01.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A03Z01C02.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C03.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A02Z01C04.tif'
]
Same would be applicable for other characters as:
imageNames.sort(comp.'A')
imageNames.sort(comp.'T') ....
Consider the following:
imageNames.sort { def a, def b ->
def rank = 0
def matcherA = (a =~ /.*(...)\.tif/)
def codeA = matcherA[0][1]
def matcherB = (b =~ /.*(...)\.tif/)
def codeB = matcherB[0][1]
// add your own comparison logic here as desired:
rank = codeA.compareTo(codeB)
rank
}
Just make the comparators separately something like this should work-
def comparator_for_c = { }
def sortedList = imageNames.sort(comparator_for_c)
Or Alternately something like this:
def comparators = [c:{}, t:{}, ...]
def sortedListForC = imageNames.sort(comparators.c)
AS sample code:
def imageNames = ['20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C01.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A01Z01C03.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A02Z01C04.tif',
'20131018PKH26DRAQ5HOECHST1_A01_T0001F001L01A03Z01C02.tif']
def sorters = [sort_c:{a, b -> b <=> a }, sort_t:{}]
println "DefaultSort ${imageNames.sort()}"
println "c sort: ${imageNames.sort(sorters.sort_c)}"
Just put your custom comparators in the sorters map and call which one you want.
I have the following C++ code
std::map<std::string, std::vector<std::vector<std::vector<double> > > > details
details["string"][index][index].push_back(123.5);
May I know what is the Pythonic to declare an empty map of vector of vector of vector? :p
I try to have
self.details = {}
self.details["string"][index][index].add(value)
I am getting
KeyError: 'string'
Probably the best way would be to use a dict for the outside container with strings for the keys mapping to an inner dictionary with tuples (the vector indices) mapping to doubles:
d = {'abc': {(0,0,0): 1.2, (0,0,1): 1.3}}
It's probably less efficient (less time-efficient at least, it's actually more space-efficient I would imagine) than actually nesting the lists, but IMHO cleaner to access:
>>> d['abc'][0,0,1]
1.3
Edit
Adding keys as you went:
d = {} #start with empty dictionary
d['abc'] = {} #insert a new string key into outer dict
d['abc'][0,3,3] = 1.3 #insert new value into inner dict
d['abc'][5,3,3] = 2.4 #insert another value into inner dict
d['def'] = {} #insert another string key into outer dict
d['def'][1,1,1] = 4.4
#...
>>> d
{'abc': {(0, 3, 3): 1.3, (5, 3, 3): 2.4}, 'def': {(1, 1, 1): 4.4}}
Or if using Python >= 2.5, an even more elegant solution would be to use defaultdict: it works just like a normal dictionary, but can create values for keys that don't exist.
import collections
d = collections.defaultdict(dict) #The first parameter is the constructor of values for keys that don't exist
d['abc'][0,3,3] = 1.3
d['abc'][5,3,3] = 2.4
d['def'][1,1,1] = 4.4
#...
>>> d
defaultdict(<type 'dict'>, {'abc': {(0, 3, 3): 1.3, (5, 3, 3): 2.4}, 'def': {(1, 1, 1): 4.4}})
Python is a dynamic (latent-typed) language, so there is no such thing as a "map of vector of vector of vector" (or "dict of list of list of list" in Python-speak). Dicts are just dicts, and can contain values of any type. And an empty dict is simply: {}
create dict that contains a nested list which inturn contains a nested list
dict1={'a':[[2,4,5],[3,2,1]]}
dict1['a'][0][1]
4
Using collections.defaultdict, you can try the following lambda trick below. Note that you'll encounter problems pickling these objects.
from collections import defaultdict
# Regular dict with default float value, 1D
dict1D = defaultdict(float)
val1 = dict1D["1"] # string key type; val1 == 0.0 by default
# 2D
dict2D = defaultdict(lambda: defaultdict(float))
val2 = dict2D["1"][2] # string and integer key types; val2 == 0.0 by default
# 3D
dict3D = defaultdict(lambda: defaultdict(lambda: defaultdict(float)))
val3 = dict3D[1][2][3] # val3 == 0.0 by default
# N-D, arbitrary nested defaultdicts
dict4D = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(str))))
val4 = dict4D["abc"][10][9][90] # val4 == '' by default
You can basically nest as many of these defaultdict collection types. Also, note that they behave like regular python dictionaries that can take the usual key types (non-mutable and hashable). Best of luck!