Wednesday, May 26, 2010

I have two collections and I need to see if one of them is a representation of the other. Since each kind of object has an Id, the two sets of Ids should match, but since collections are unordered, you can't just do a pairwise match.

The solution is to use the containsAll method to see if each collection contains all the elements of the other one. In Scheme this is trivial:
(let ((foo-ids (map foo-id (get-foos)))
      (bar-ids (map bar-id (get-bars))))
  (and (contains-all foo-ids bar-ids)
       (contains-all bar-ids foo-ids)))
in Java, not so easy:
Collection<Id> fooIds =
    Collections2.transform(getFoos(),
                           new Function<Foo, Id> () {
                             @Override
                             public Id apply (Foo foo) {
                               return foo.getId();
                             }
                           });
Collection<Id> barIds =
    Collections2.transform(getBars(),
                           new Function<Bar, Id> () {
                             @Override
                             public Id apply (Bar bar) {
                               return bar.getId();
                             }
                           });
return fooIds.containsAll(barIds) &&
       barIds.containsAll(fooIds);
I know that this is not Java's forte, but this isn't some obscure stratified monadic combinator, it's just map for heaven's sake! If future maintainers of this code can't handle map, maybe they should consider a career in the fast food industry.

5 comments:

grant rettke said...

Hi Joe,

It is immediately obvious what it does though isn't it? :) It is 'map' in Java's language... yay.

Funny in a way, "ugly" too by some definition.

Best wishes,

Grant Rettke

MGB said...

Comparator byStringValue = new Comparator() {
public int compare(Object o1, Object o2) {
return String.valueOf(o1).compareTo(String.valueOf(o2));
}
};
TreeSet strings = new TreeSet(byStringValue);
TreeSet longs = new TreeSet(byStringValue);

strings.addAll(Arrays.asList("1", "2", "3"));
longs.addAll(Arrays.asList(3L, 2L, 1L));

return strings.equals(longs);

John Cowan said...

A translation of the Java containsAll algorithm into Scheme using external iterators wouldn't be very pretty either. To each language its own genius loci.

JBF said...

So what is the minimal set of changes to the language that would make you happy enough (or perhaps only mildly annoyed)?

JRH said...

You could always add kawa-1.9.90.jar to your project and then write the map parts in Kawa Scheme.

;; utils.scm
(define (foo-id (foo :: Foo)) :: Id
 (foo:getId))
(define (bar-id (bar :: Bar)) :: Id
 (bar:getId))

(define (foo-ids foos)
  (map foo-id foos))

(define (bar-ids bars)
  (map bar-id bars))

// *.java
Collection fooIds = utils.fooIds(foos);
Collection barIds = utils.barIds(bars);

return fooIds.containsAll(barIds) &&   barIds.containsAll(fooIds);