views:

29

answers:

1

I'm trying to figure out the best approach to display combined tables based on matching logic and input search criteria.

Here is the situation:

We have a table of customers stored locally. The fields of interest are ssn, first name, last name and date of birth.

We also have a web service which provides the same information. Some of the customers from the web service are the same as the local file, some different.

SSN is not required in either.

I need to combine this data to be viewed on a Grails display.

The criteria for combination are 1) match on SSN. 2) For any remaining records, exact match on first name, last name and date of birth.

There's no need at this point for soundex or approximate logic.

It looks like what I should do is extract all the records from both inputs into a single collection, somehow making it a set on SSN. Then remove the blank ssn.

This will handle the SSN matching (once I figure out how to make that a set).

Then, I need to go back to the original two input sources (cached in a collection to prevent a re-read) and remove any records that exist in the SSN set derived previously.

Then, create another set based on first name, last name and date of birth - again if I can figure out how to make a set.

Then combine the two derived collections into a single collection. The collection should be sorted for display purposes.

Does this make sense? I think the search criteria will limit the number of record pulled in so I can do this in memory.

Essentially, I'm looking for some ideas on how the Grails code would look for achieving the above logic (assuming this is a good approach). The local customer table is a domain object, while what I'm getting from the WS is an array list of objects.

Also, I'm not entirely clear on how the maxresults, firstResult, and order used for the display would be affected. I think I need to read in all the records which match the search criteria first, do the combining, and display from the derived collection.

+1  A: 

The traditional Java way of doing this would be to copy both the local and remote objects into TreeSet containers with a custom comparator, first for SSN, second for name/birthdate.

This might look something like:

def localCustomers = Customer.list()
def remoteCustomers = RemoteService.get()
TreeSet ssnFilter = new TreeSet(new ClosureComparator({c1, c2 -> c1.ssn <=> c2.ssn}))
ssnFilter.addAll(localCustomers)
ssnFilter.addAll(remoteCustomers)
TreeSet nameDobFilter = new TreeSet(new ClosureComparator({c1, c2 -> c1.firstName + c1.lastName + c1.dob <=> c2.firstName + c2.lastName + c2.dob}))
nameDobFilter.addAll(ssnFilter)
def filteredCustomers = nameDobFilter as List

At this point, filteredCustomers has all the records, except those that are duplicates by your two criteria.

Another approach is to filter the lists by sorting and doing a foldr operation, combining adjacent elements if they match. This way, you have an opportunity to combine the data from both sources.

For example:

def combineByNameAndDob(customers) {
    customers.sort() { 
        c1, c2 -> (c1.firstName + c1.lastName + c1.dob) <=> 
                  (c2.firstName + c2.lastName + c2.dob)
    }.inject([]) { cs, c -> 
        if (cs && c.equalsByNameAndDob(cs[-1])) {
            cs[-1].combine(c)  //combine the attributes of both records
            cs
        } else {
            cs << c
        }
    }
}
ataylor
Jack BeNimble
One problem I ran into was the fact the remote customer was coming from a web service, so the the resulting object was a java object. This caused the comparitor to crash. I copied the java object to a groovy class which mirrored it and the comparitor worked fine after that.
Jack BeNimble