commit c11a18edd96128979609382adc8357bf72bf55dd
Author: Rodrigo Moya <rodrigo@gnome.org>
Date:   Wed Dec 28 01:22:56 2016 +0100

    Add some more query operations for Traversable<G>
    
    * first_match (Predicate<G>) returns the first item that matches
    * any_match (Predicate<G>) checks if any element matches
    * all_match (Predicate<G>) checks if all elements match
    * max/min returns max/min value
    * order_by to perform ordering on any Traversable

diff --git a/gee/traversable.vala b/gee/traversable.vala
index 77ce6ea..1903aed 100644
--- a/gee/traversable.vala
+++ b/gee/traversable.vala
@@ -446,6 +446,130 @@ public interface Gee.Traversable<G> : Object {
 		}
 	}
 
+	/**
+	 * Returns the first element that matches a given condition
+	 *
+	 * @param pred Predicate to be called to check for matches
+	 * @return The first element that matches or null
+	 * @since 0.18.2
+	 */
+	[CCode (ordering = 10)]
+	public virtual G first_match (owned Predicate<G> pred) {
+		G result = null;
+		this.foreach ((item) => {
+			if (pred (item)) {
+				result = item;
+				return false;
+			}
+
+			return true;
+		});
+		
+		return (owned) result;
+	}
+
+	/**
+	 * Returns whether any element matches the given predicate.
+	 *
+	 * This is similar to @first_match, with the difference that it
+	 * just returns whether there is a match or not, not the value
+	 * of the match.
+	 *
+	 * @param pred Predicate to be called to check for matches
+	 * @return Whether there was a match or not
+	 * @since 0.18.2
+	 */
+	[CCode (ordering = 11)]
+	public virtual bool any_match (owned Predicate<G> pred) {
+		return this.first_match (pred) != null;
+	}
+
+	/**
+	 * Checks whether all elements match the given predicate.
+	 *
+	 * @param pred Predicate to be called to check for matches
+	 * @return Whether all elements match or not
+	 * @since 0.18.2
+	 */
+	[CCode (ordering = 12)]
+	public virtual bool all_match (owned Predicate<G> pred) {
+		bool result = true;
+		this.foreach ((item) => {
+			if (!pred (item)) {
+				result = false;
+				return false;
+			}
+
+			return true;
+		});
+
+		return result;
+	}
+
+	/**
+	 * Returns the item in the sequence that contains the max value
+	 * based on the given compare function.
+	 *
+	 * @param compare Function to be called for comparisons
+	 * @return The item containing the max value.
+	 * @since 0.18.2
+	 */
+	[CCode (ordering = 13)]
+	public virtual G max (owned CompareDataFunc<G> compare) {
+		G max_value = null;
+		this.foreach ((item) => {
+			if (max_value == null || compare (max_value, item) > 0) {
+				max_value = item;
+			}
+
+			return true;
+		});
+
+		return max_value;
+	}
+
+	/**
+	 * Returns the item in the sequence that contains the min value
+	 * based on the given compare function.
+	 *
+	 * @param compare Function to be called for comparisons
+	 * @return The item containing the min value.
+	 * @since 0.18.2
+	 */
+	[CCode (ordering = 14)]
+	public virtual G min (owned CompareDataFunc<G> compare) {
+		G min_value = null;
+		this.foreach ((item) => {
+			if (min_value == null || compare (min_value, item) < 0) {
+				min_value = item;
+			}
+
+			return true;
+		});
+
+		return min_value;
+	}
+
+	/**
+	 * Returns a new iterator containing the elements in the source
+	 * ordered as specified by the comparison function.
+	 *
+	 * @param compare Comparison function
+	 * @return A new iterator with the source elements sorted.
+	 * @since 0.18.2
+	 */
+	[CCode (ordering = 15)]
+	public virtual Iterator<G> order_by (owned CompareDataFunc<G>? compare = null)
+	{
+		ArrayList<G> result = new ArrayList<G> ();
+
+		this.foreach ((item) => result.add (item));
+
+		result.sort (compare);
+
+		return result.iterator ();
+	}
+
 	public enum Stream {
 		YIELD,
 		CONTINUE,
diff --git a/tests/testcollection.vala b/tests/testcollection.vala
index 268724a..819e130 100644
--- a/tests/testcollection.vala
+++ b/tests/testcollection.vala
@@ -51,6 +51,11 @@ public abstract class CollectionTests : Gee.TestCase {
 		add_test ("[Collection] scan", test_scan);
 		add_test ("[Collection] filter", test_filter);
 		add_test ("[Collection] chop", test_chop);
+		add_test ("[Collection] first_match", test_first_match);
+		add_test ("[Collection] any_match", test_any_match);
+		add_test ("[Collection] all_match", test_all_match);
+		add_test ("[Collection] max_min", test_max_min);
+		add_test ("[Collection] order_by", test_order_by);
 	}
 
 	protected Collection<string> test_collection;
@@ -1197,5 +1202,62 @@ public abstract class CollectionTests : Gee.TestCase {
 		assert (!iter.next ());
 		assert (iter2.next ());
 	}
+
+	public void test_first_match () {
+		assert (test_collection.add ("one"));
+		assert (test_collection.add ("two"));
+		assert (test_collection.add ("three"));
+
+		assert (test_collection.first_match ((x) => x == "one") == "one");
+		assert (test_collection.first_match ((x) => x == "two") == "two");
+		assert (test_collection.first_match ((x) => x == "three") == "three");
+		assert (test_collection.first_match ((x) => x == "four") == null);
+	}
+
+	public void test_any_match () {
+		assert (test_collection.add ("one"));
+		assert (test_collection.add ("two"));
+		assert (test_collection.add ("three"));
+
+		assert (test_collection.any_match ((x) => x == "one"));
+		assert (test_collection.any_match ((x) => x == "two"));
+		assert (test_collection.any_match ((x) => x == "three"));
+		assert (!test_collection.any_match ((x) => x == "four"));
+	}
+
+	public void test_all_match () {
+		assert (test_collection.add ("one"));
+		assert (test_collection.all_match ((x) => x == "one"));
+
+		assert (test_collection.add ("two"));
+		assert (!test_collection.all_match ((x) => x == "one"));
+	}
+
+	public void test_max_min () {
+		assert (test_collection.add ("one"));
+		assert (test_collection.add ("two"));
+		assert (test_collection.add ("three"));
+
+		assert (test_collection.max ((a, b) => strcmp (a, b)) == "one");
+		assert (test_collection.min ((a, b) => strcmp (a, b)) == "two");
+	}
+
+	public void test_order_by () {
+		assert (test_collection.add ("one"));
+		assert (test_collection.add ("two"));
+		assert (test_collection.add ("three"));
+
+		var sorted_collection = test_collection.order_by ((a, b) => strcmp (a, b));
+
+		string previous_item = null;
+		while (sorted_collection.next ()) {
+			var item = sorted_collection.get ();
+			if (previous_item != null) {
+				assert (strcmp (previous_item, item) <= 0);
+			}
+
+			previous_item = item;
+		}
+	}
 }
 
