Solving a ranking problem in a purely functional way using groovy - groovy

I have solved a problem that ranks fruits by the number of votes. Unfortunately, I want to solve the problem in a purely functional way without mutating the rankPosition variable. Here is my solution:
def fruits=[
[name:'apple', votes:120 , ranking:null ],
[name:'banana', votes:200, ranking: null],
[name:'apricot', votes:66, ranking:null ],
[name:'pear', votes:84, ranking:null],
[name:'kiwi', votes:77, ranking:null],
[name:'plum', votes:66, ranking:null],
[name:'berry', votes:120, ranking:null],
[name:'pineapple', votes:50, ranking:null],
[name:'grapes', votes:200, ranking:null]
]
def rankPosition= 1
def groupedByVotes = fruits.groupBy {it.votes }
println "Ratings $groupedByVotes"
def finalResults=groupedByVotes.sort().each { votes, items ->
items.each { it.ranking = rankPosition }
rankPosition += items.size()
}
println "Final Results are $finalResults"
How can I solve this problem without having to declare a rankingPosition variable external to the closure and mutating its state. Please notes that this solution works but I have since learned that I shouldn't be doing it this way.
I want to be able to fill the rankings with the correct ranking. The inject function does an accumulation but I don't know how to combine it in a way to also set the ranking with the value accumulated in the inject.
I am simply stuck, just don't seem to be able to reason about this one. My attempt below to use inject, simply did not work. Maybe there isn't a way to do this in a purely functional way, better thsn my attempt.
def res= groupedByVotes.collectEntries{votes, list1->
println "list class $list1"
def r= list1.inject(0){acc,l-> acc+l.size()}
list1.each{it.ranking=r}
println "$r"
[(votes): list1]
}
println "$res"
I anyone can then I would appreciate your solution or just assume my attempt is the most realistic way of solving this one.

This is a pure functional solution. It leaves the initial map of maps unchanged and produces a new one:
def results = groupedByVotes.sort().inject(new Tuple(1, [:]), { acc, entry ->
def newRank = acc[0] + entry.value.size()
def newValue = entry.value.collect { [*:it, ranking:acc[0]] }
return new Tuple(newRank, [*:acc[1], (entry.key):newValue] )
})
finalResults = results[1]

I probably did not explain very well what I was trying to achieve. After calculating the rank, I wanted the values inserted for the respective elements in the list. Here is what I came up with:
def sortedFruits= fruits.sort{f1, f2 -> f1.votes <=> f2.votes}
(0..sortedFruits.size()-1)
.each{ i ->
if(i==0){
sortedFruits.get(i).ranking=1
}else if(sortedFruits.get(i-1).votes==sortedFruits.get(i).votes){
sortedFruits.get(i).ranking=i
}else{
sortedFruits.get(i).ranking=i+1
}
if(i<sortedFruits.size()){
def f= sortedFruits.get(i)
println "${f}"
}
}
println "Sorted Fruits are $sortedFruits"
The final result looks like
Sorted Fruits are [[name:lemons, votes:20, ranking:1], [name:guava, votes:20, ranking:1],
[name:pineapple, votes:50, ranking:3], [name:apricot, votes:66, ranking:4], [
name:plum, votes:66, ranking:4] etc.

You can try this:
def results = groupedByVotes.sort()
.inject(new Tuple(1, []), { acc, entry ->
entry.value.each { it.ranking = acc[0] }
return new Tuple(acc[0] + entry.value.size(), acc[1] << entry.value)
})
finalResults = results[1]
In each step of the folding (done by .inject(...)) you have a Tuple containing the next rank and the partial list that has been computed so far. As a final step you extract the result list from the Tuple. But this converts the map to a list.
This solution is even simpler because it is not necessary to make a new collection if you modify the old one in place, and it preserves the map:
def finalResults = groupedByVotes.sort()
finalResults.inject(1, { acc, entry ->
entry.value.each { it.ranking = acc }
return acc + entry.value.size()
})
But both solutions are not really functional. Real functional code treats all values as if they were immutable. See my other answer (coming) for a real functional solution.

Related

Python3: make a chain call of functions

This question could be senseless, it's just interesting for me is it possible ot not.
In my code I had a lot of similar checks and calls like:
if self.command == self.CMD_HELP:
help(self.id)
if self.command == self.CMD_FIND:
find(self.id)
...
I found that in theory it could be done as:
self.COMMANDS = {
'help': help,
'find': find,
...
}
And execution:
self.COMMANDS[command](self.id)
It's fine if I will call one function.
But what if I need to call something like first(second(arg)) is it possible?
Update:
Sorry, my initial description appeared not very clear.
All this stuff is about the refactoring of the current implementation:
if command == MessageSettings.MSG_GLOBAL_HELP:
notify_help(guid)
if command == MessageSettings.MSG_GLOBAL_DISCORD:
sv_async.submit_fast(sv_discord.notify_discord_help(guid))
if command == MessageSettings.MSG_GLOBAL_FIND:
sv_async.submit_fast(sv_discord.notify_found_discord_users(guid, param))
... a lot of other 'ifs'
In the same time I have to support the list of the values to make the comparison:
class MessageSettings:
MSG_GLOBAL_HELP = 'help'
MSG_GLOBAL_DISCORD = 'discord'
MSG_GLOBAL_FIND = 'find'
...
MSG_VALUES = [
MSG_GLOBAL_HELP,
MSG_GLOBAL_DISCORD,
MSG_GLOBAL_FIND,
...
]
In this case if I add new option I will have to modify 3 places: new field in the class, add it into the array, add comparison and execution of if == value -> function.
Since functions can receive different amount of parameters I can rewrite all functions to take an array [] as a singe param.
I thought of using a dictionary with a "key -> function".
My question was that I did not understand if that was possible to apply this approach in cases if multiple function calls.
Also I don't know and I am not sure if such approach worth it.
ps: in my 3rd case there are:
def submit_fast(task):
future = asyncio.run_coroutine_threadsafe(task, loop_fast)
def notify_found_discord_users(will_replace_with_array):
anything
You could use lamda as in :
def help (n):
return "help " + n + " "
def find (n):
return "find " + n + " "
COMMANDS = {
'help': help,
'find': find,
'other' : (lambda x: help(x) + find(x))
}
print (COMMANDS['other']("Joe"))
If you mean something like this,
def lower(x):
return x.lower()
def upper(x):
return x.upper()
a = "a string"
b = upper(lower(upper(a)))
Of course.
the output of an inner function will be chained as the input of its outer function (as long as their parameters match).
It's the fundamental of any programming language.

Create a list of lists from two maps with values of the common keys

There are two different maps. One is bigger than the other. The keys of small map is always a subset of keys of the bigger map. I want to take the values for keys that are common to both maps and create a list of lists using Groovy features. The objective is to achieve it with as little code as possible with Groovy features.
Map big = ['FirstName':'first_name', 'LastName':'last_name', 'FullName':'full_name']
Map small = ['FirstName':'John', 'FullName':'John Williams']
println Output
[[first_name, John], [full_name, John Williams]]
//works even if the small map is not a subset completely
def result = []
big.keySet().intersect(small.keySet()).each {
result << [big[it], small[it]]
}
assert [['first_name', 'John'], ['full_name', 'John Williams']] == result
EDIT: Added two variations to the solution suggested by #taiyebur
// VARIATION 2
def result2 = big.subMap(small.keySet()).collect([]) {
[it.value, small.get(it.key)]
}
assert [['first_name', 'John'], ['full_name', 'John Williams']] == result2
// VARIATION 3
def result3 = small.collect([]) {
[big[it.key], it.value]
}
assert [['first_name', 'John'], ['full_name', 'John Williams']] == result3

Chain (compose) method calls in Groovy

I have a variable and several methods I'd like to call in a sequence where return value of one method is an input of another. Basically a pipeline. Now, is there a way of chaining the calls? In a pseudo code it looks like
def a = [1, 2, 3]
def b = calculation3(calculation2(calculation1(a)))
As you can see it looks very Clojure-like and I'd like to end up with something like (using Clojure syntax)
(-> a
calculation1
calculation2
calculation3)
I was hoping to use the with keyword but it only passes the variable a around not collecting the results and passing them as the input to the next method.
The only working solution I've found so far is 'closure composition' like this. But that seems to be too heavy handed to me.
def a = [1, 2, 3]
def b = (class1.&calculation1 >> class1.&calculation2 >> class1.&calculation3)(a)
Any idea?
You could write a function to do it:
def chain(a, Closure... fns) {
fns.toList().inject(a) { v, c -> c(v) }
}
Then call this in your code:
chain(a, class1.&calculation1,class1.&calculation2,class1.&calculation3)
I gave a bit more thought and have come up with something like this:
def compose = {...c ->
{result ->
c.each {
result = it(result)
}
result
}
}
def a = [1, 2, 3]
def b = compose(class1.&calculation1, class1.&calculation2, class1.&calculation3)(a)
Still it's not as nice as Clojure's threading.
Use inject
def pipe(fn, ...fns) {
{ args ->
fns.inject(fn(args)) { acc, value ->
value(acc)
}
}
}
Not that the first function is allowed to take a variable number of arguments while the following parameters are not.

groovy - is there any implicit variable to get access to item index in "each" method

Is there any way to remove variable "i" in the following example and still get access to index of item that being printed ?
def i = 0;
"one two three".split().each {
println ("item [ ${i++} ] = ${it}");
}
=============== EDIT ================
I found that one possible solution is to use "eachWithIndex" method:
"one two three".split().eachWithIndex {it, i
println ("item [ ${i} ] = ${it}");
}
Please do let me know if there are other solutions.
you can use eachWithIndex()
"one two three four".split().eachWithIndex() { entry, index ->
println "${index} : ${entry}" }
this will result in
0 : one
1 : two
2 : three
3 : four
Not sure what 'other solutions' you are looking for... The only other thing you can do that I can think of (with Groovy 1.8.6), is something like:
"one two three".split().with { words ->
[words,0..<words.size()].transpose().collect { word, index ->
word * index
}
}
As you can see, this allows you to use collect with an index as well (as there is no collectWithIndex method).
Another approach, if you want to use the index of the collection on other methods than each is to define an enumerate method that returns pairs [index, element], analog to Python's enumerate:
Iterable.metaClass.enumerate = { start = 0 ->
def index = start
delegate.collect { [index++, it] }
}
So, for example:
assert 'un dos tres'.tokenize().enumerate() == [[0,'un'], [1,'dos'], [2,'tres']]
(notice that i'm using tokenize instead of split because the former returns an Iterable, while the later returns a String[])
And we can use this new collection with each, as you wanted:
'one two three'.tokenize().enumerate().each { index, word ->
println "$index: $word"
}
Or we can use it with other iteration methods :D
def repetitions = 'one two three'.tokenize().enumerate(1).collect { n, word ->
([word] * n).join(' ')
}
assert repetitions == ['one', 'two two', 'three three three']
Note: Another way of defining the enumerate method, following tim_yates' more functional approach is:
Iterable.metaClass.enumerate = { start = 0 ->
def end = start + delegate.size() - 1
[start..end, delegate].transpose()
}

Is there any way to leverage Groovy's collect method in combination with another iterator function?

For example, the groovy File class has a nice iterator that will filter out just directories and not files:
void eachDir(Closure closure)
When I use eachDir, I have to use the verbose method of creating the collection first and appending to it:
def collection = []
dir1.eachDir { dir ->
collection << dir
}
Any way to get it back to the nice compact collect syntax?
I don't know of any "idiomatic" way of doing this, nice riddle! =D
You can try passing the eachDir, or any similar function, to a function that will collect its iterations:
def collectIterations(fn) {
def col = []
fn {
col << it
}
col
}
And now you can use it as:
def dir = new File('/path/to/some/dir')
def subDirs = collectIterations(dir.&eachDir)
def file = new File('/path/to/some/file')
def lines = collectIterations(file.&eachLine)
(that last example is equivalent to file.readLines())
And only for bonus points, you may define this function as a method in the Closure class:
Closure.metaClass.collectIterations = {->
def col = []
delegate.call {
col << it
}
col
}
def dir = new File('/path/to/some/dir')
def subDirs = dir.&eachDir.collectIterations()
def file = new File('/path/to/some/file')
def lines = file.&eachLine.collectIterations()
Update: On the other hand, you might also do:
def col = []
someDir.eachDir col.&add
Which I think is quite less convoluted, but it's not leveraging the collect method as you requested :)
Not for the specific example that you're talking about. File.eachDir is sort of a weird implementation IMO. It would have been nice if they implemented iterator() on File so that you could use the normal iterator methods on them rather than the custom built ones that just execute a closure.
The easiest way to get a clean one liner that does what you're looking for is to use listFiles instead combined with findAll:
dir1.listFiles().findAll { it.directory }
If you look at the implementation of eachDir, you'll see that it's doing this (and a whole lot more that you don't care about for this instance) under the covers.
For many similar situations, inject is the method that you'd be looking for to have a starting value that you change as you iterate through a collection:
def sum = [1, 2, 3, 4, 5].inject(0) { total, elem -> total + elem }
assert 15 == sum

Resources