maidh91 / guava-libraries

Automatically exported from code.google.com/p/guava-libraries
Apache License 2.0
0 stars 0 forks source link

Ordering.explicit should handle equals values not only strictly ordered values, and should provide default lowest or highest order for unknown values #332

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
I need explicit ordering with elements that are equals such as

<pre>
Ordering.explicit(a, [b1, b2], c).compare(b1, b2) returns 0
</pre>

explicit signature should be discussed, may be 
Ordering.explicit(List<Iterable<T>> elements) should be enough ?

I need also ordering other non-explicit elements such as

<pre>
Ordering.explicit(UNKNOWN_LOWER, a, [b1, b2], c).compare(d, a) returns -1

or

Ordering.explicit(UNKNOWN_GREATER, a, [b1, b2], c).compare(d, a) returns 1
</pre>

because right now, com.google.common.collect.ExplicitOrdering.rank(T) 
throws IncomparableValueException when the T value is known

I joined a first implementation that handle equals elements fully inspired 
from package visible com.google.common.collect.ExplicitOrdering

What do you think of this need ?

Original issue reported on code.google.com by amer...@gmail.com on 24 Feb 2010 at 1:10

Attachments:

GoogleCodeExporter commented 9 years ago
edit:
because right now, com.google.common.collect.ExplicitOrdering.rank(T) 
throws IncomparableValueException when the T value is UNknown

Original comment by amer...@gmail.com on 24 Feb 2010 at 3:49

GoogleCodeExporter commented 9 years ago
EDIT : when the T value is UNKNOWN

Original comment by amer...@gmail.com on 24 Feb 2010 at 3:51

GoogleCodeExporter commented 9 years ago
On the a-b1-b2-c case, can you provide a more full illustration of what you're 
really doing, so we can think 
about whether there's another way to do it?  I'm not sure that adding a complex 
new overload to support this 
case would pay for itself in the API.

On handling unknown values, I think there's a chain of orderings you can use to 
accomplish this:

Ordering.explicit(...).nullsFirst().onResultOf(UNKNOWN_TO_NULL_FUNCTION);

It's not very compact, but if this is not a very common operation, that's fine.

Original comment by kevinb@google.com on 27 Apr 2010 at 2:45

GoogleCodeExporter commented 9 years ago
The way to handle unknown values is fine. It's a nice thought I should had.

It would be great to let extends-able package final class 
com.google.common.collect.ExplicitOrdering to simplify customization.

About the a-b1-b2-c case, it happened because we used enum to describe some 
business 
data type.

Let's take an example :

we have enum RABBIT_TYPE { WHITE_RABBIT, SMALL_RABBIT, GRAY_RABBIT, BIG_RABBIT}.

The categorization is wrong among this type because we mixed different kind of 
physical attribute (color and height) but we cannot change that easily.

So, for us, color attribute are equals and height attribute are equals but they 
all 
are of the same enum type. Maybe we could create RABBIT_COLOR_TYPE and so on, 
but java 
don't let extends enum and we are stuck with all legacy code that use these 
enum type.

So we'd like to use Ordering.explicit([WHITE_RABBIT, GRAY_RABBIT], 
[SMALL_RABBIT, 
BIG_RABBIT])

Hope it helps.

Original comment by amer...@gmail.com on 27 Apr 2010 at 3:49

GoogleCodeExporter commented 9 years ago
There are two independent issues here; handling unknowns, and handling equality.

For equality, I agree that the function is probably the way to go. So for the 
case mentioned, it could have:

Ordering.explicit(GRAY_RABBIT, BIG_RABBIT).onResultOf(MAP_EQUALS)

where MAP_EQUALS maps WHITE_RABBIT => GRAY_RABBIT, etc.

It might be worth adding a convenience method

onResultOf(Map<F,? extends T> map) 

For the null case, I agree that it would be more handy to have something 
built-in, rather than have to repeat the list (and take the extra lookup cost) 
in a separate function. Maybe something like:

exceptionsFirst() // any item that would cause a ClassCastException is sorted 
first (see nullsFirst)
exceptionsLast() // any item that would cause a ClassCastException is sorted 
last (see nullsLast)

or

exceptionsAsNulls() // items causing exceptions are treated as nulls for 
ordering.

Original comment by markda...@google.com on 25 Aug 2010 at 5:29

GoogleCodeExporter commented 9 years ago
I want to provide a default ordering for the "nulls".

Example: I have a Map<String, Object> containing a bunch of labeled fields of 
userdata. There are some common fields that are expected to be present like 
"userId", or "email", but may not be, and there may be many more fields, some 
added in the future.

I want an ordering where "userId" comes first, then "email", followed by any 
other fields in alphabetical order.

Does that clarify the use case?

What I ended up doing was destructively modifying the map: iterating through a 
List of "known fields", and adding them in order and removing them from the 
map. Then I sorted the remaining keys and added those.

Devil's advocate: probably the proper way to do this is to have one's "known 
fields" in an Enum, with every possible field listed, plus one at the end 
called CUSTOM. Then have a FieldName class that has one of two constructors: 
one that takes the enum (and validates that null or CUSTOM is not passed-in) 
and one that takes a String (for custom fields). This class is Comparable and 
sorts first by enum and then by the String...

Original comment by ray.j.gr...@gmail.com on 14 Oct 2010 at 8:16

GoogleCodeExporter commented 9 years ago
At this point I am generally sympathetic to the notion that Ordering.explicit 
as currently implemented is insufficient, and we should come up with the thing 
that can do all the things that users need.  I don't know what that will look 
like exactly, yet, but am open to hear specific proposals 

Original comment by kevinb@google.com on 16 Oct 2010 at 5:26

GoogleCodeExporter commented 9 years ago

Original comment by fry@google.com on 26 Jan 2011 at 10:45

GoogleCodeExporter commented 9 years ago
Some methods signature ideas to handle equality :

as for this ordering : WHITE_RABBIT = GRAY_RABBIT < SMART_RABBIT < FAST_RABBIT 
< SMALL_RABBIT = BIG_RABBIT

Ordering.explicit(newArrayList(WHITE_RABBIT, GRAY_RABBIT), 
newArrayList(SMART_RABBIT), newArrayList(FAST_RABBIT), 
newArrayList(SMALL_RABBIT, BIG_RABBIT));
Ordering.explicit(Iterable<Iterable<T>> elements)

or using newArrayList alias

Ordering.explicit(eq(WHITE_RABBIT, GRAY_RABBIT), explicit(SMART_RABBIT, 
FAST_RABBIT), eq(SMALL_RABBIT, BIG_RABBIT));

or method chaining (looks nice)

Ordering.explicit().eq(WHITE_RABBIT, GRAY_RABBIT).explicit(SMART_RABBIT, 
FAST_RABBIT).eq(SMALL_RABBIT, BIG_RABBIT);

or builder

Ordering.explicitBuilder().ascending().eq(WHITE_RABBIT, 
GRAY_RABBIT).then(SMART_RABBIT, FAST_RABBIT).eq(SMALL_RABBIT, 
BIG_RABBIT).build();
Ordering.explicitBuilder().eq(WHITE_RABBIT, 
GRAY_RABBIT).lowerThan(SMART_RABBIT, FAST_RABBIT).eq(SMALL_RABBIT, 
BIG_RABBIT).build();

Which one do you prefer ? Other ideas ?

Original comment by amer...@gmail.com on 1 Feb 2011 at 10:12

GoogleCodeExporter commented 9 years ago

Original comment by kevinb@google.com on 13 Jul 2011 at 6:18

GoogleCodeExporter commented 9 years ago

Original comment by kevinb@google.com on 16 Jul 2011 at 8:37

GoogleCodeExporter commented 9 years ago
I'd like to be able to this:
Ordering.explicit(List<T>).unknownsLast().compound(Comparator<T>)

So that I can order by the list, then by the comparator when the item isn't in 
the list. Currently I have to add all remaining possible items to the end of 
the list pre-sorted by the second Comparator.

Original comment by goo...@sa.me.uk on 10 Nov 2011 at 10:53

GoogleCodeExporter commented 9 years ago

Original comment by fry@google.com on 10 Dec 2011 at 3:44

GoogleCodeExporter commented 9 years ago

Original comment by kevinb@google.com on 30 May 2012 at 7:43

GoogleCodeExporter commented 9 years ago
As I have just run into the problem with handling unknown values I am 
interested in helping with a fix.

The proposed solution earlier in this thread requires a lot of unnecessary 
code, and needs to use the explicit list twice in the setup chain:

Ordering.explicit(...).nullsFirst().onResultOf(UNKNOWN_TO_NULL_FUNCTION);

I think this proposed syntax is a good solution in keeping with the general 
chained approach to building Orderings.

Ordering.explicit(List<T>).unknownsLast().compound(Comparator<T>)

In order to achieve this and to leverage the existing rank map within 
ExplicitOrdering we need to have access to this.  I think the neatest way to do 
this would be to make an inner class of ExplicitOrdering that acts (similarly 
to nullsFirstOrdering) as a guard and uses the rank map to check for the 
existence of left and right before deferring to ExplicitOrdering.

I would propose the following API changes to allow explicit ordering to take 
this form in order to handle unknown objects.

1) Change the visibility of ExplicitOrdering<T> to public
2) Change the factory methods for .explicit(...) in Collection to return 
ExplicitOrdering<T> rather than Ordering<T>
3) Add the following methods to ExplicitOrdering<T>

public Ordering<T> unknownsFirst()
public Ordering<T> unknownsLast()

I have a working prototype for this including tests if anyone is interested in 
looking at it.

Original comment by drme...@gmail.com on 1 Oct 2013 at 9:43

GoogleCodeExporter commented 9 years ago
This issue has been migrated to GitHub.

It can be found at https://github.com/google/guava/issues/<id>

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:15

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:19

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 3 Nov 2014 at 9:10