views:

145

answers:

5

I have a List> which is straight forward representation of a database table. I am trying to sort and apply some magic after the data is loaded into List of HashMaps. In my case this is the only hard and fast way of doing it becoz I have a rules engine that actually updates the values in the HashMap after several computations.

Here is a sample data representation of the HashMap (List of HashMap) -

{fromDate=Wed Mar 17 10:54:12 EDT 2010, eventId=21, toDate=Tue Mar 23 10:54:12 EDT 2010, actionId=1234}
{fromDate=Wed Mar 17 10:54:12 EDT 2010, eventId=11, toDate=Wed Mar 17 10:54:12 EDT 2010, actionId=456}
{fromDate=Sat Mar 20 10:54:12 EDT 2010, eventId=20, toDate=Thu Apr 01 10:54:12 EDT 2010, actionId=1234}
{fromDate=Wed Mar 24 10:54:12 EDT 2010, eventId=22, toDate=Sat Mar 27 10:54:12 EDT 2010, actionId=1234}
{fromDate=Wed Mar 17 10:54:12 EDT 2010, eventId=11, toDate=Fri Mar 26 10:54:12 EDT 2010, actionId=1234}
{fromDate=Sat Mar 20 10:54:12 EDT 2010, eventId=11, toDate=Wed Mar 31 10:54:12 EDT 2010, actionId=1234}
{fromDate=Mon Mar 15 10:54:12 EDT 2010, eventId=12, toDate=Wed Mar 17 10:54:12 EDT 2010, actionId=567}

I am trying to achieve couple of things -

1) Sort the list by actionId and eventId after which the data would look like -

{fromDate=Wed Mar 17 10:54:12 EDT 2010, eventId=11, toDate=Wed Mar 17 10:54:12 EDT 2010, actionId=456}
{fromDate=Mon Mar 15 10:54:12 EDT 2010, eventId=12, toDate=Wed Mar 17 10:54:12 EDT 2010, actionId=567}
{fromDate=Wed Mar 24 10:54:12 EDT 2010, eventId=22, toDate=Sat Mar 27 10:54:12 EDT 2010, actionId=1234}
{fromDate=Wed Mar 17 10:54:12 EDT 2010, eventId=21, toDate=Tue Mar 23 10:54:12 EDT 2010, actionId=1234}
{fromDate=Sat Mar 20 10:54:12 EDT 2010, eventId=20, toDate=Thu Apr 01 10:54:12 EDT 2010, actionId=1234}
{fromDate=Wed Mar 17 10:54:12 EDT 2010, eventId=11, toDate=Fri Mar 26 10:54:12 EDT 2010, actionId=1234}
{fromDate=Sat Mar 20 10:54:12 EDT 2010, eventId=11, toDate=Wed Mar 31 10:54:12 EDT 2010, actionId=1234}

2) If we group the above list by actionId they would be resolved into 3 groups - actionId=1234, actionId=567 and actionId=456. Now here is my question -

For each group having the same eventId, I need to update the records so that they have wider fromDate to toDate.

Meaning, if you consider the last two rows they have same actionId = 1234 and same eventId = 11. Now we can to pick the least fromDate from those 2 records which is Wed Mar 17 10:54:12 and farther toDate which is Wed Mar 31 10:54:12 and update those 2 record's fromDate and toDate to Wed Mar 17 10:54:12 and Wed Mar 31 10:54:12 respectively.

Any ideas?

PS: I already have some pseudo code to start with.

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang.builder.CompareToBuilder;
public class Tester {
    boolean ascending = true ;
    boolean sortInstrumentIdAsc = true ;
    boolean sortEventTypeIdAsc = true ; 

    public static void main(String args[]) {
        Tester tester = new Tester() ;
        tester.printValues() ;
    }

    public void printValues ()
    {

        List<HashMap<String,Object>> list = new ArrayList<HashMap<String,Object>>() ;
        HashMap<String,Object> map = new HashMap<String,Object>();

        map.put("actionId", new Integer(1234)) ;
        map.put("eventId", new Integer(21)) ;
        map.put("fromDate", getDate(1) ) ;
        map.put("toDate", getDate(7) ) ;
        list.add(map);

        map = new HashMap<String,Object>();
        map.put("actionId", new Integer(456)) ;
        map.put("eventId", new Integer(11)) ;
        map.put("fromDate", getDate(1)) ;
        map.put("toDate", getDate(1) ) ;
        list.add(map);


        map = new HashMap<String,Object>();
        map.put("actionId", new Integer(1234)) ;
        map.put("eventId", new Integer(20)) ;
        map.put("fromDate", getDate(4) ) ;
        map.put("toDate", getDate(16) ) ;
        list.add(map);

        map = new HashMap<String,Object>();
        map.put("actionId", new Integer(1234)) ;
        map.put("eventId", new Integer(22)) ;
        map.put("fromDate",getDate(8) ) ;
        map.put("toDate", getDate(11)) ;
        list.add(map);


        map = new HashMap<String,Object>();
        map.put("actionId", new Integer(1234)) ;
        map.put("eventId", new Integer(11)) ;
        map.put("fromDate",getDate(1) ) ;
        map.put("toDate", getDate(10) ) ;
        list.add(map);

        map = new HashMap<String,Object>();
        map.put("actionId", new Integer(1234)) ;
        map.put("eventId", new Integer(11)) ;
        map.put("fromDate",getDate(4) ) ;
        map.put("toDate", getDate(15) ) ;
        list.add(map);


        map = new HashMap<String,Object>();
        map.put("actionId", new Integer(567)) ;
        map.put("eventId", new Integer(12)) ;
        map.put("fromDate", getDate(-1) ) ;
        map.put("toDate",getDate(1)) ;
        list.add(map);


        System.out.println("\n Before Sorting \n ");
        for(int j = 0 ; j < list.size() ; j ++ ) 
            System.out.println(list.get(j));    

        Collections.sort ( list , new HashMapComparator2 () ) ;

        System.out.println("\n After Sorting \n ");
        for(int j = 0 ; j < list.size() ; j ++ ) 
            System.out.println(list.get(j));

    }


    public static Date getDate(int days) {

        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DATE, days);
        return cal.getTime() ;        

    }

    public class HashMapComparator2 implements Comparator
    {
        public int compare ( Object object1 , Object object2 )
        {
            if ( ascending == true )
            {
                return new CompareToBuilder()
                .append(( ( HashMap ) object1 ).get ( "actionId" ), ( ( HashMap ) object2 ).get ( "actionId" ))
                .append(( ( HashMap ) object2 ).get ( "eventId" ), ( ( HashMap ) object1 ).get ( "eventId" ))
                .toComparison();
            }
            else
            {
                return new CompareToBuilder()
                .append(( ( HashMap ) object2 ).get ( "actionId" ), ( ( HashMap ) object1 ).get ( "actionId" ))
                .append(( ( HashMap ) object2 ).get ( "eventId" ), ( ( HashMap ) object1 ).get ( "eventId" ))
                .toComparison();
            }
        }
    }


}
A: 

You want to sort your list ? Then use a TreeSet and a customized Comparator. This way, each time a Map is added, it will be set at the correct position.

Notice that, if you want to change sort algorithm during list usage, you can instead use Collections#sort.

And finally notice I consider your way of sorting things quite weird, since sorting your DB data can usually be done more consistently by using a SORT predicate in your SQL statement.

Riduidel
A TreeSet assumes that the objects will not be changed after insertion (at least in any way that affects the sort order). The asker specifically states that he needs to update the values in the HashMaps and then resort them again.
Avi
Then using Collections.sort should get the point.
Riduidel
I am already using Collection.sort in my code. Now my issue is to iterate through the list and update fromDate and toDate!
HonorGod
Do you mean your problem has nothing to do with data sorting, like your text says, but with batch updating a collection ?
Riduidel
i am already able to sort the list....the only issue remaining is updating each group with my requirement [see the condition in my original posting]
HonorGod
+2  A: 

As I understand from your description all your data is retrieved from DB. Why don't you do sorting and grouping stuff by means of SQL?

UPD (after comment): then I definitely like a solution with

TreeMap<Integer, List<DbRecord>> 

where actionIds are keys of this TreeMap and each item of your list is DbRecord object.

In this case sorting and grouping problems will be solved implicitly, and you'll have only to iterate through the map in order to update dates values.

Even better approach is to use TreeMultimap from Google Collections.

Roman
Yea, I know experts would ask me this, but as I mentioned in my post, there is some processing that is done by a custom Rules Engine which will update the values in the HashMap after several iterations after which the values are different than the ones stored in database. So is the reason I can not sort them in db and load.
HonorGod
A: 

It sounds like you really want to just have one object per actionId/ eventId pair. Have you considered instead using something like a factory to generate a new object / modify an existing object? Rough code:

public class ObjectFactory{

  class Key{
    String eventId, actionId;
  }

  HashMap<Key, ObjectXYZ> objects = new HashMap<...,...>();

  ObjectXYZ getObject(String actionId, String eventId, Date from, Date to){
    Key k = new Key(actionId, eventId);
    ObjectXYZ ret = objects.get(k);
    if(ret == null){
      ret = new ObjectXYZ(actionid, eventId, from, to);
      objects.put(k, ret);
    }else{
      if(from < ret.from)  ret.from = from;
      if(to < ret.to) ret.to = to;
    }
    return ret;
  }

}

Then you would not need to create any extra objects and would have fewer objects to sort (if you would need to sort them at all).

M. Jessup
when the HashMap is initially constructed it doesn't have fromDate and toDate. They are updated by a different process....
HonorGod
A: 

A number of posts suggest a Custom Comparator and using natural sorting provided by a treeset or treemap, e.g

public class MapComparator implements Comparator<Map>{
   public MapComparator(String key, boolean asc){..set value properties ...}
   public int comparae(Map a, Map b) { ... compare a.get(key), b.get(key) ... }
}

Since your data is also changing on the fly, I think you have an additional complication, in that reordering your collection of maps for one operation may invalidate the ordering for a previous operation. If you're doing operations serially, and once you've done a sort you don't need to preserve the values, that's fine, but if there's ever any point where you'll simultaneously need the access to 2 different sort results it will break, since each sort overwrites (or at least can potentially break) the previous. For that you could either

  • sort deep copies of the maps, create a complete copy of the set(s) for each operation. This could get really expensive if your data sets are large.
  • pull in the maps and generate a key for each. Leave the original collection of maps as immutable. For your individual comparison sorts, sort the keys, reference the immutable collection( i.e. maintain the sort data separately from the map data)
Steve B.
Steve B >> would you be able to modify my code by providing some pseudo code?
HonorGod
A: 
HonorGod