Smalltalk - iterate over a dict array with conditions - smalltalk

I'm working on a Smalltalk small method, I want this method to iterate over a dictionary array and to return True or False depend on the conditions.
The dictionary array is an instance variable, name dictArray.
It looks like: [{'name': toto, 'age': 12}, {'name': tata, 'age': 25}]
So I want to iterate over dictArray and verify for each item the name and the age. If it matches I return true else false and the end of the iteration.
In python it should look like:
for item in dictArray:
if item['name'] == aName and item['age'] == aAge:
return True
return False
I can't find documentation with this special case (array iteration + condition + return)
Hope someone can help me!

To test whether a Collection contains an element that matches a condition, use anySatisfy:. It answers true iff there is a matching element.
dictArray anySatisfy: [:each | (each at: 'name') = aName and: [(each at: 'age') = anAge]]
Reference: https://www.gnu.org/software/smalltalk/manual-base/html_node/Iterable_002denumeration.html
The way described above is the preferred way to write it. The following is only for explanation how it relates to your Python code example.
anySatisfy: can be implemented in terms of do:
anySatisfy: aBlock
self do: [:each | (aBlock value: each) ifTrue: [^ true]].
^ false
Or spelled out with your condition:
dictArray do:
[:each |
((each at: 'name') = aName and: [(each at: 'age') = anAge])
ifTrue: [^ true]].
^ false
This is the equivalent of your Python code.

Related

PyQt QPushButton clicked connect lambda can't get correct value [duplicate]

This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 2 years ago.
Problemset:
Context Menu should show filter variables dynamically and execute a function with parameters defined inside the callback.
Generic descriptions show properly, but function call is always executed with last set option.
What I have tried:
#!/usr/bin/env python
import Tkinter as tk
import ttk
from TkTreectrl import MultiListbox
class SomeClass(ttk.Frame):
def __init__(self, *args, **kwargs):
ttk.Frame.__init__(self, *args, **kwargs)
self.pack(expand=True, fill=tk.BOTH)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.View=MultiListbox(self)
__columns=("Date","Time","Type","File","Line","-","Function","Message")
self.View.configure(columns=__columns, expandcolumns=(0,0,0,0,0,0,0,1))
self.View.bind("", self.cell_context)
self.View.grid(row=0, column=0, sticky=tk.NW+tk.SE)
self.__recordset = []
self.__recordset_filtered = False
#Some dummy values
self.__recordset.append(["Date", "Time", "INFO", "File", "12", "-", "Function", "Message Info"])
self.__recordset.append(["Date", "Time", "DEBUG", "File", "12", "-", "Function", "Message Info"])
self.__recordset.append(["Date", "Time", "WARNING", "File", "12", "-", "Function", "Message Info"])
self.__refresh()
def cleanView(self):
self.View.delete(0, tk.END)
def __refresh(self):
self.cleanView()
for row in self.__recordset:
self.View.insert(tk.END, *row)
def filter_records(self, column, value):
print("Filter Log Recordset by {column} and {value}".format(**locals()))
# Filter functionality works as expected
# [...]
def cell_context(self, event):
__cMenu=tk.Menu(self, tearoff=0)
if self.__recordset_filtered:
__cMenu.add_command(label="Show all", command=lambda: filter_records(0, ""))
else:
column=2
options=["INFO", "WARNING", "DEBUG"]
for i in range(len(options)):
option=options[i]
__cMenu.add_command(label="{}".format(option), command=lambda: self.filter_records(column, option))
# Also tried using for option in options here with same result as now
__cMenu.post(event.x_root, event.y_root)
if __name__=="__main__":
root=tk.Tk()
app=SomeClass(root)
root.mainloop()
The current output i get is:
Filter Log Recordset by 2 and DEBUG
No matter which of the three options i choose. I assume it has sth to do with the garbage collection that only the last option remains but i cannot figure out how to avoid this.
Any help is recommended.
Please read about minimal examples. Without reading your code, I believe you have run into a well known issue addressed in previous questions and answers that needs 2 lines to illustrate. Names in function bodies are evaluated when the function is executed.
funcs = [lambda: i for i in range(3)]
for f in funcs: print(f())
prints '2' 3 times because the 3 functions are identical and the 'i' in each is not evaluated until the call, when i == 2. However,
funcs = [lambda i=i:i for i in range(3)]
for f in funcs: print(f())
makes three different functions, each with a different captured value, so 0, 1, and 2 are printed. In your statement
__cMenu.add_command(label="{}".format(option),
command=lambda: self.filter_records(column, option))
add option=option before : to capture the different values of option. You might want to rewrite as
lambda opt=option: self.filter_records(column, opt)
to differentiate the loop variable from the function parameter. If column changed within the loop, it would need the same treatment.
Closures in Python capture variables, not values. For example consider:
def f():
x = 1
g = lambda : x
x = 2
return g()
What do you expect the result of calling f() to be? The correct answer is 2, because the lambda f captured the variable x, not its value 1 at the time of creation.
Now if for example we write:
L = [(lambda : i) for i in range(10)]
we created a list of 10 different lambdas, but all of them captured the same variable i, thus calling L[3]() the result will be 9 because the value of variable i at the end of the iteration was 9 (in Python a comprehension doesn't create a new binding for each iteration; it just keeps updating the same binding).
A "trick" that can be seen often in Python when capturing the value is the desired semantic is to use default arguments. In Python, differently from say C++, default value expressions are evaluated at function definition time (i.e. when the lambda is created) and not when the function is invoked. So in code like:
L = [(lambda j=i: j) for i in range(10)]
we're declaring a parameter j and setting as default the current value of i at the time the lambda was created. This means that when calling e.g. L[3]() the result will be 3 this time because of the default value of the "hidden" parameter (calling L[3](42) will return 42 of course).
More often you see the sightly more confusing form
lambda i=i: ...
where the "hidden" parameter has the same name as the variable of which we want to capture the value of.
I know I am late, but I found a messy workaround which gets the job done (tested in Python 3.7)
If you use a double lambda (like I said, very messy) you can preserve the value, like so:
Step 1: Create the nested lambda statement:
send_param = lambda val: lambda: print(val)
Step 2: Use the lambda statement:
send_param(i)
The send_param method returns the inner most lambda (lambda: print(val)) without executing the statement, until you call the result of send_param which takes no arguments, for example:
a = send_param(i)
a()
Only the second line will execute the print statement.

Compact way to save JuMP optimization results in DataFrames

I would like to save all my variables and dual variables of my finished lp-optimization in an efficient manner. My current solution works, but is neither elegant nor suited for larger optimization programs with many variables and constraints because I define and push! every single variable into DataFrames separately. Is there a way to iterate through the variables using all_variables() and all_constraints() for the duals? While iterating, I would like to push the results into DataFrames with the variable index name as columns and save the DataFrame in a Dict().
A conceptual example would be for variables:
Result_vars = Dict()
for vari in all_variables(Model)
Resul_vars["vari"] = DataFrame(data=[indexval(vari),value(vari)],columns=[index(vari),"Value"])
end
An example of the appearance of the declared variable in JuMP and DataFrame:
#variable(Model, p[t=s_time,n=s_n,m=s_m], lower_bound=0,base_name="Expected production")
And Result_vars[p] shall approximately look like:
t,n,m,Value
1,1,1,50
2,1,1,60
3,1,1,145
Presumably, you could go something like:
x = all_variables(model)
DataFrame(
name = variable_name.(x),
Value = value.(x),
)
If you want some structure more complicated, you need to write custom code.
T, N, M, primal_solution = [], [], [], []
for t in s_time, n in s_n, m in s_m
push!(T, t)
push!(N, n)
push!(M, m)
push!(primal_solution, value(p[t, n, m]))
end
DataFrame(t = T, n = N, m = M, Value = primal_solution)
See here for constraints: https://jump.dev/JuMP.jl/stable/constraints/#Accessing-constraints-from-a-model-1. You want something like:
for (F, S) in list_of_constraint_types(model)
for con in all_constraints(model, F, S)
#show dual(con)
end
end
Thanks to Oscar, I have built a solution that could help to automatize the extraction of results.
The solution is build around a naming convention using base_name in the variable definition. One can copy paste the variable definition into base_name followed by :. E.g.:
#variable(Model, p[t=s_time,n=s_n,m=s_m], lower_bound=0,base_name="p[t=s_time,n=s_n,m=s_m]:")
The naming convention and syntax can be changed, comments can e.g. be added, or one can just not define a base_name. The following function divides the base_name into variable name, sets (if needed) and index:
function var_info(vars::VariableRef)
split_conv = [":","]","[",","]
x_str = name(vars)
if occursin(":",x_str)
x_str = replace(x_str, " " => "") #Deletes all spaces
x_name,x_index = split(x_str,split_conv[1]) #splits raw variable name+ sets and index
x_name = replace(x_name, split_conv[2] => "")
x_name,s_set = split(x_name,split_conv[3])#splits raw variable name and sets
x_set = split(s_set,split_conv[4])
x_index = replace(x_index, split_conv[2] => "")
x_index = replace(x_index, split_conv[3] => "")
x_index = split(x_index,split_conv[4])
return (x_name,x_set,x_index)
else
println("Var base_name not properly defined. Special Syntax required in form var[s=set]: ")
end
end
The next functions create the columns and the index values plus columns for the primal solution ("Value").
function create_columns(x)
col_ind=[String(var_info(x)[2][col]) for col in 1:size(var_info(x)[2])[1]]
cols = append!(["Value"],col_ind)
return cols
end
function create_index(x)
col_ind=[String(var_info(x)[3][ind]) for ind in 1:size(var_info(x)[3])[1]]
index = append!([string(value(x))],col_ind)
return index
end
function create_sol_matrix(varss,model)
nested_sol_array=[create_index(xx) for xx in all_variables(model) if varss[1]==var_info(xx)[1]]
sol_array=hcat(nested_sol_array...)
return sol_array
end
Finally, the last function creates the Dict which holds all results of the variables in DataFrames in the previously mentioned style:
function create_var_dict(model)
Variable_dict=Dict(vars[1]
=>DataFrame(Dict(vars[2][1][cols]
=>create_sol_matrix(vars,model)[cols,:] for cols in 1:size(vars[2][1])[1]))
for vars in unique([[String(var_info(x)[1]),[create_columns(x)]] for x in all_variables(model)]))
return Variable_dict
end
When those functions are added to your script, you can simply retrieve all the solutions of the variables after the optimization by calling create_var_dict():
var_dict = create_var_dict(model)
Be aware: they are nested functions. When you change the naming convention, you might have to update the other functions as well. If you add more comments you have to avoid using [, ], and ,.
This solution is obviously far from optimal. I believe there could be a more efficient solution falling back to MOI.

What does [:] mean in groovy?

While reading some groovy code of another developer I encountered the following definition:
def foo=[:]
What does it mean?
[:] is shorthand notation for creating a Map.
You can also add keys and values to it:
def foo = [bar: 'baz']
[:] creates an empty Map. The colon is there to distinguish it from [], which creates an empty List.
This groovy code:
def foo = [:]
is roughly equivalent to this java code:
Object foo = new java.util.LinkedHashMap();
Quoting the doc:
Notice that [:] is the empty map expression.
... which is the only Map with size() returning 0. ) By itself, it's rarely useful, but you can add values into this Map, of course:
def emptyMap = [:]
assert emptyMap.size() == 0
emptyMap.foo = 5
assert emptyMap.size() == 1
assert emptyMap.foo == 5

Grails query rows to arrays

I'm new to Groovy and Grails. I think this problem probably has an easy answer, I just don't know it.
I have a database table:
id | category | caption | image | link
I have a query that lets me retrieve one row for each distinct item in 'category.'
I'm trying to return a map where each row is an array named by it's category.
e.g., If I select the rows:
[{category='foo', caption='stuff', ...} {category='bar', caption='things', ...}]
I want to be able to:
return [foo:foo, bar:bar]
where:
foo = [caption='stuff', ...]
bar = [caption='things', ...]
Thanks for any help.
You can transform your list using collect in Groovy, however, the result depends on the source data. I could not infer from your post that if you are returning one item per category or multiple.
def ls = Category.list()
def newList = ls.collect {
[(it.category):['caption':it.caption,'image':it.image,'link':it.link]]
}
will result in something like :
[
[bar:[caption:BarCaption-2, image:BarImage-2, link:BarLink-2]],
[bar:[caption:BarCaption-1, image:BarImage-1, link:BarLink-1]],
[bar:[caption:BarCaption-0, image:BarImage-0, link:BarLink-0]],
[foo:[caption:FooCaption-2, image:FooImage-2, link:FooLink-2]],
[foo:[caption:FooCaption-1, image:FooImage-1, link:FooLink-1]],
[foo:[caption:FooCaption-0, image:FooImage-0, link:FooLink-0]]
]
If you have multiple items per each category you probably want to return the list of each.
def bars = newList.findAll { it.containsKey 'bar' }
def foos = newList.findAll { it.containsKey 'foo' }
[foos:foos,bars:bars]
If I understand your question correctly (I think you mean to put ':' instead of '=' for your maps) then I think the following will work. If new to Groovy, you might find the Groovy web console at http://groovyconsole.appspot.com/ useful. You can try snippets of code out easily, like the following:
def listOfMaps = [[category:'foo', caption:'stuff', something:'else'], [category:'bar', caption:'things', another:'thing']]
def mapOfMaps = [:]
listOfMaps.each { mapOfMaps += [(it.remove('category')) : it] }
assert mapOfMaps == [foo:[caption:'stuff', something:'else'], bar:[caption:'things', another:'thing']]

How to iterate over a map created in MVEL

I have created a map in MVEL and I have to iterate over it using foreach. How would I do that?
There is a similar question:
How to iterate over a map in mvel
But in that case the map was created in Java and had a method to return array of keys (entrySet) which is not the case with me.
//MVEL
map = [
'a': 'a1',
'b': 'b2',
'c': 'c3'
];
foreach (key: map) {
System.out.println(key);
}
I have tried both map and map.entrySet in the foreach loop but none seems to work.
Note: I test it using MVEL command line and using MVEL version 2.2.0.15
Although you have accepted an answer, I think it is better to add something as not to mislead other people:
... had a method to return array of keys (entrySet) which is not the case with me
First, Map is a Map. Map created in MVEL is simply a "Java" Map. The way to iterate is just the same and they are providing same methods
Second, entrySet() is not returning "array of keys". It is returning a Set of Entry (as its name suggests).
I am not sure why you cannot use entrySet as it works just fine for me. I suspect you have do foreach (e : map.entrySet). That will not work, because in MVEL, property navigation can mean several thing, like bean properties (which means it will call map.getEntrySet()), or looking up a map (which means it will call map.get('entrySet')), or getting the field (which means 'map.entrySet'). However all these are not valid for your case. You simply want to invoke map.entrySet() method so that you should just do foreach (e : map.entrySet())
The proper way to do is something like this:
map = ['a':'a1', 'b':'b1'] ;
foreach(entry : map.entrySet()) {
System.out.println('key ' + entry.key + ' value ' + entry.value)
};
You can use something like this:
map = [
'a': 'a1',
'b': 'b2',
'c': 'c3'
];
foreach (key : map.keySet()) {
System.out.println("Key:" + key + " Value:" + map[key]);
}
It outputs:
Key:b Value:b2
Key:c Value:c3
Key:a Value:a1