Felipe
Felipe

Reputation: 440

Aggregating value from java ArrayList in an elegant way

I have a list of objects which I want to aggregate one of the values of this object grouped by other values of the objects in this list.

I'm currently using the properties I want to group by as a Hash key and I'm traversing the object so:

ArrayList<MyObject> raw = Some Data;
Map<String, MyObject> map = new HashMap<String, MyObject>();

for (MyObject ungrouped : raw) {
  String key = ungrouped.getStringOne().getName() + ungrouped.getStringTwo() + ungrouped.getStringThree();

  if (map.containsKey(key)){
    MyObject holder = map.get(key);
    holder.setNumericProp(holder.getNumericProp() + ungrouped.getNumericProp());
    // map.put(key, holder); //Edited after comments
  }
  else{
    map.put(key, ungrouped);
  }
}
return map.values().toArray(new MyObject[map.values().size()]);

Is there a more elegant way to do this without using the concatenated strings as a key?

If this was SQL (from which I'm several application layers away) it would be:

SELECT SUM(numericvalue) FROM sometable GROUP BY stringone, stringtwo , stringthree

Upvotes: 0

Views: 1813

Answers (2)

fge
fge

Reputation: 121780

Apart from some problems I see with the code, one solution would be to use (if you can afford it) Guava's Equivalence (or replicate it in your code). You'd implement an Equivalence<MyObject> and use a Map<Equivalence.Wrapper<MyObject>, MyObject> as a container; you'd make the equivalence on your three string members.

That would allow it not to break in this situation:

// Oops! Same key...
s1 = "foo", s2 = "bar", s3 = "baz"
s1 = "fooba", s2 = "rb", s3 = "az"

Also, you could use the return value of the map's .put() method (the old value):

MyObject holder = map.put(key, ungrouped);
if (holder != null)
    holder.setNumericProp(etc);

Upvotes: 2

Gaston Flores
Gaston Flores

Reputation: 2467

If your looking elegantly solve this you can use lambdja libraries (Download Here, Website). For example you can SUM a column with the following code (look at this link):

double sum = sumFrom(select(sales, 
             having(on(Sale.class).getBuyer().isMale())
             .and( having(on(Sale.class).getSeller().isMale())))).getCost();

You also can group with this (look at this link):

Group<Person> group = group(meAndMyFriends, by(on(Person.class).getAge()));

With this libraries you can solve your problem in few lines.

Upvotes: 0

Related Questions