indexOfSorted Improvement


#1

It would be nice if indexOfSorted had an extra template parameter for the objectToLookFor type. This way you could do fancy things like search a list of objects for a specific attribute. (assuming, of course, that the list is already sorted based on that attribute)


#2

Surely it could already do that if you gave it a suitable custom comparator?


#3

Lets say I have a class like

struct Data { int value1; String name; float value2; };
, and I want to search a list of these for one that has the name “foobar”. What I would like to do is have a method like

int compareElements(const String& n, Data* d) { return n.compare(d->name); }
and search with

array.indexOfSorted(*this, "foobar");
, but what I have to do now is either create a separate Data object with the name set to “foobar” or use nasty pointer casting.


#4

Hmm, that feels a bit wrong somehow… I can see what you’re trying to do, but using a temporary object that matches the thing you’re looking for is certainly the “correct” way to do it. Otherwise it makes a lot of assumptions about the ordering, and just feels like it’d lead to bug-prone code…


#5

Actually, both ways feel wrong to me. If Data has no default constructor than you would have to add one (which might cause probems if used for anything but comparing) or create temporary values to pass into the constructor (which seems like a terrible hack).

On the other hand, with my way, the information about how the array is sorted is spread out over two methods: one ElementComparator to sort the list in the first place and one ElementComparator to do lookups.

I think the second way would be more flexible though. You could then define a class like

[code]template <class ElemType, class ReturnType, class ElemComparator>
class CompareOn {
private:
const ElemComparator& comp;

public:
CompareOn(const ElemComparator& comp) : comp(comp) {}

int compareElements(ElemType a, ElemType b) {
return comp.compareElements(comp.getComparableValue(a), comp.getComparableValue(b));
}

int compareElements(ReturnType a, ElemType b) {
return comp.compareElements(a, comp.getComparableValue(b));
}
};[/code]

Then all you would have to do is define

String getComparableValue(Data* d) {
  return d->name;
}

int compareElements(const String& a, const String& b) {
  return a.compare(b);
}

and use

OwnedArray<Data> arr; CompareOn<Data, String, MyClassType> comp(*this); arr.sort(comp); arr.indexOfSorted(comp, "foobar");


#6

Now that I look at it, that way might be a bit overarchitected. :lol: Still, creating a temporary object doesn’t give me warm fuzzy feelings.

If C++ had closures, it would make sense to just pass in a function that took one object as a parameter and returned -1/0/1 based on whether or not the object you’re looking for is earlier, at, or later than that point in the list. (In ruby you could do something like array.index_of_sorted {|x| x.name <=> ‘foobar’}) But since it doesn’t, that way would be rather cumbersome to use.


#7

wait for C++0x then, it will have lambda expressions.


#8

Whoo! (even though the syntax is a bit strange)

So jules, how about redesigning juce to use lambda functions instead of listeners? :lol:


#9

I don’t think I’ve ever used one before - I’d need to get some practice first!