I'm new to Fantom, and I've only played around with it for a day or so. I'm having a bit of trouble figuring out how to write a certain function.
The idea is to have a function consuming a list of A, and returning a map from B => lists of A, where the B keys are given by calling a supplied function, F, with A as an argument. So it's sort of like "grouping" the elements in a list depending on some property on each object.
Perhaps code samples are clearer... :-)
The equivalent Ruby code could look something like:
def group(list, func)
m = {}
list.each do |e|
k = func.call(e)
m[k] ||= []
m[k] << e
end
m
end
p group([1,2,3,4,5,6,7,8,9], lambda {|a| a % 2 == 0 ? :even : :odd})
Output:
{:odd=>[1, 3, 5, 7, 9], :even=>[2, 4, 6, 8]}
(There are probably prettier ways to do that; I'm new to Ruby too.)
And in Java you could write it as :
import java.util.*;
public class Group {
static interface Unary<T, U> {
public U call(T arg);
}
static <K, V> Map<K, List<V>> group(Collection<V> coll, Unary<V, K> fun) {
Map<K, List<V>> map = new HashMap<K, List<V>>();
for (V v : coll) {
K key = fun.call(v);
List<V> list = map.get(key);
if (list == null) {
list = new ArrayList<V>();
map.put(key, list);
}
list.add(v);
}
return map;
}
public static void main(String[] args) {
System.out.println(group(
Arrays.asList(1,2,3,4,5,6,7,8,9),
new Unary<Integer, String>() {
public String call(Integer arg) {
return arg % 2 == 0 ? "even" : "odd";
}
}));
}
}
Output:
{even=[2, 4, 6, 8], odd=[1, 3, 5, 7, 9]}
How would I write an equivalent general group() function in Fantom?
DanielFathTue 7 Dec 2010
First off Fantom doesn't support generic programming so I'm gonna reduce the domain to Int[] and map of Str:Int[]. Something like this:
However, the complete translation of Ruby code to Fantom could look like this:
Obj:Obj?[] group(Obj?[] list, |Obj?->Obj| func)
{
m := Obj:Obj?[][:] //m := [:]
list.each |e|
{
k := func(e)
m[k] = m[k] ?: [,]
m[k].add(e) //m[k]->add(e)
}
return m
}
Replacing lines with comments would correspond to solution with dynamic invoke
DanielFathTue 7 Dec 2010
I'm a Java programmer, can't you tell :P
ahhatemTue 7 Dec 2010
class Main
{
static Void main()
{
echo(group([1,2,3,4,5,6,7,8,9], |Int e->Str|{ return e % 2==0?"even" : "Odd"}))
}
static Obj:Obj[] group (Obj[] list, |Obj e->Obj| func)
{
Obj:Obj[] map := [:]
list.each |Obj e|{
k := func(e)
map[k] = map[k] ?: [,]
// if (!map.containsKey(k)) map[k] = Obj[,]
map[k].add(e)
}
return map
}
}
perpTue 7 Dec 2010
Great answers, everyone, thanks! I'm starting to like this language. :-)
vkuzkokovTue 7 Dec 2010
Actually m.getOrAdd(k) { Int[,] } is closer than m[k] = m[k] ?: [,] because there's no unnecessary set in the former. The flipside is an extra closure.
BTW, Ivan's example optimizes away second get. Ruby equivalent would be (AFAIK it's valid Ruby) (m[k] ||= []) << e.
perp Tue 7 Dec 2010
I'm new to Fantom, and I've only played around with it for a day or so. I'm having a bit of trouble figuring out how to write a certain function.
The idea is to have a function consuming a list of A, and returning a map from B => lists of A, where the B keys are given by calling a supplied function, F, with A as an argument. So it's sort of like "grouping" the elements in a list depending on some property on each object.
Perhaps code samples are clearer... :-)
The equivalent Ruby code could look something like:
def group(list, func) m = {} list.each do |e| k = func.call(e) m[k] ||= [] m[k] << e end m end p group([1,2,3,4,5,6,7,8,9], lambda {|a| a % 2 == 0 ? :even : :odd})Output:
{:odd=>[1, 3, 5, 7, 9], :even=>[2, 4, 6, 8]}(There are probably prettier ways to do that; I'm new to Ruby too.)
And in Java you could write it as :
import java.util.*; public class Group { static interface Unary<T, U> { public U call(T arg); } static <K, V> Map<K, List<V>> group(Collection<V> coll, Unary<V, K> fun) { Map<K, List<V>> map = new HashMap<K, List<V>>(); for (V v : coll) { K key = fun.call(v); List<V> list = map.get(key); if (list == null) { list = new ArrayList<V>(); map.put(key, list); } list.add(v); } return map; } public static void main(String[] args) { System.out.println(group( Arrays.asList(1,2,3,4,5,6,7,8,9), new Unary<Integer, String>() { public String call(Integer arg) { return arg % 2 == 0 ? "even" : "odd"; } })); } }Output:
{even=[2, 4, 6, 8], odd=[1, 3, 5, 7, 9]}How would I write an equivalent general group() function in Fantom?
DanielFath Tue 7 Dec 2010
First off Fantom doesn't support generic programming so I'm gonna reduce the domain to
Int[]and map ofStr:Int[]. Something like this:class Group { static Str:Int[] group(Int[] list, |Int->Str| func) { retVal := Str:Int[][:] list.each |Int i| { s := retVal[func.call(i)] if(s != null && !s.isEmpty) { s.add(i) } else { retVal.getOrAdd(func(i)) { [i] } } } return retVal } static Void main(Str[] args) { echo(group([1,2,3,4,5,6,7,8,9], |Int i->Str|{return i%2 == 0 ? "even" : "odd" } )) } }Note:
func.call(i)andfunc(i)are the same.func(i)is just a shortcut forcallmethod.ivan Tue 7 Dec 2010
Writing in DanielFath's domain, I'd rather write it like this:
list := (0..<20).toList func := |Int i->Str| { (i % 5).toStr } result := list.reduce([:]) |Str:Int[] r, Int val->Str:Int[]| { r { getOrAdd(func(val)) |->Int[]| { Int[,] }.add(val) } } echo(result)prints:
However, the complete translation of Ruby code to Fantom could look like this:
Obj:Obj?[] group(Obj?[] list, |Obj?->Obj| func) { m := Obj:Obj?[][:] //m := [:] list.each |e| { k := func(e) m[k] = m[k] ?: [,] m[k].add(e) //m[k]->add(e) } return m }Replacing lines with comments would correspond to solution with dynamic invoke
DanielFath Tue 7 Dec 2010
I'm a Java programmer, can't you tell :P
ahhatem Tue 7 Dec 2010
class Main { static Void main() { echo(group([1,2,3,4,5,6,7,8,9], |Int e->Str|{ return e % 2==0?"even" : "Odd"})) } static Obj:Obj[] group (Obj[] list, |Obj e->Obj| func) { Obj:Obj[] map := [:] list.each |Obj e|{ k := func(e) map[k] = map[k] ?: [,] // if (!map.containsKey(k)) map[k] = Obj[,] map[k].add(e) } return map } }perp Tue 7 Dec 2010
Great answers, everyone, thanks! I'm starting to like this language. :-)
vkuzkokov Tue 7 Dec 2010
Actually
m.getOrAdd(k) { Int[,] }is closer thanm[k] = m[k] ?: [,]because there's no unnecessarysetin the former. The flipside is an extra closure.BTW, Ivan's example optimizes away second
get. Ruby equivalent would be (AFAIK it's valid Ruby)(m[k] ||= []) << e.