5

What's C#'s equivalence of the following Python's min/max code:

pairs = [ (2,"dog"), (1, "cat"), (3, "dragon"), (1, "tiger") ]

# Returns the PAIR (not the number) that minimizes on pair[0]
min_pair = min(pairs, key=lambda pair:pair[0])

# this will return (1, 'cat'), NOT 1

It seems that C#'s Enumerable.Min is very close. But according to its MSDN doc, it always returns the minimizing VALUE (not the original object). Am I missing anything?

EDIT


Please note - I'm not inclined to achieve this by sorting first, since sorting (O(nlogn)) is computationally heavier than finding the minimum (O(n)).

Please also note - Dictionary is not a desired approach either. It cannot handle cases where there are duplicate keys - (1, "cat") and (1, "tiger").

More importantly, dictionary cannot handle cases where the items to be processed is a complex class. E.g., finding minimum over a list of animal objects, using age as the key:

class Animal
{
  public string name;
  public int age;
}
sloth
  • 99,095
  • 21
  • 171
  • 219
KFL
  • 17,162
  • 17
  • 65
  • 89
  • According to MSDN doc, you can assign the result type by setting the TResult parameter. `public static TResult Min`, it seems you are misunderstanding the doc. – Mayli May 29 '12 at 05:37
  • 1
    possible duplicate of [minimum value in dictionary using linq](http://stackoverflow.com/questions/3653970/minimum-value-in-dictionary-using-linq) – Preet Sangha May 29 '12 at 05:45
  • 1
    Refer to this http://stackoverflow.com/questions/914109/how-to-use-linq-to-select-object-with-minimum-or-maximum-property-value – lahsrah May 29 '12 at 05:54
  • Dont forget to mark answer as accepted if you got the info you want.... – Pranay Rana May 29 '12 at 11:46

4 Answers4

3

The BCL doesn't have a MinBy function, but it's easy to write one yourself.

public static T MinBy<T, C>(this IEnumerable<T> items, Func<T, C> projection) where C : IComparable<C> {
    return items.Aggregate((acc, e) => projection(acc).CompareTo(projection(e)) <= 0 ? acc : e);
}

You may choose to write a more complex MinBy than me, in order to avoid re-evaluating the projection. In any case, once you have your MinBy function you can easily solve the problem:

var pairs = new[] {Tuple.Create(2,"dog"), Tuple.Create(1, "cat"), Tuple.Create(3, "dragon"), Tuple.Create(1, "tiger")};
var min_pair = pairs.MinBy(e => e.Item1);
Craig Gidney
  • 17,763
  • 5
  • 68
  • 136
  • THanks! This is very close to what I'm looking for. I'm a little surprised as C# does not support such a useful and common operation originally. – KFL May 30 '12 at 04:40
0

EDIT

var minage = collection.Min( x => x.Age ); //for maxage replace Min by Max
var minAgeAnimals = collection.where(x=> x.age == minage); 
foreach(Animal animal in minAgeAnimals )
   Console.Writeline (  animal.Age.ToString() + " : " + animal.Name); 

Prev. Answered before edit of question

Make use of dictonary object in C# and than do something like this does the same thing you want

int minimumKey = touchDictionary.Keys.Min(); 
string value = "";
touchDictionary.TryGetValue(minimumKey, out value))
Console.Writeline ( "min key pair is:-" + minimumKey.ToString() + " : " + value); 

or

With the help of the linq its become to easy for you

var dictionary = new Dictionary<int, string>  
                     {{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}  };  

        var maxKey = dictionary.Max(x => x.Key);  
        var minkey = dictionary.Min(x => x.Key);  
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
  • 1
    The OP seems to be looking for the object with the smallest value rather than the value itself. – Paul Sasik May 29 '12 at 05:41
  • 1
    Why use `TryGetValue` when you know the key exists (as you obtained it via `Min()`, which would throw an exception if there were no keys)? – Ed S. May 29 '12 at 05:53
  • Thanks for the reply. Please see the updated question as why Dictionary is not desired. – KFL May 29 '12 at 05:57
  • I haven't rated you (up or down) but I'd guess it's because the OP specifically says that the "key" part of it can have duplicates, and thus dictionaries wouldn't work for them. – Kevin Anderson May 29 '12 at 07:07
  • @Kevin - thats y i made edit ans suggested another way...check my edit...dictory is option before updating OP question... – Pranay Rana May 29 '12 at 07:11
0

Use

Dictionary<int, string> pairs = new Dictionary<int, string>()
                          { {2,"dog"}, {1, "cat"}, {3, "dragon"} };

var min = pairs.OrderBy(x => x.Key).FirstOrDefault();

OR

int min = pairs.Keys.Min();

Dictionary<int, string> result 
                          = new Dictionary<int, string>() { {min, pairs[min]} };
Nikhil Agrawal
  • 47,018
  • 22
  • 121
  • 208
  • Thanks. But sorting is computationally more time consuming. Dictionary cannot handle cases where there are duplicate keys. See the updated question. – KFL May 29 '12 at 05:53
0

I would use

var min = pairs.OrderBy(x => x.FirstValue).FirstOrDefault();

and while I agree that sorting is heavier than finding the minimum value please note that this is not sorting the entire set. It is finding the first (or default) item in an ordered enumeration over the set - which is iterated lazily.

If you had

var min = pairs.OrderBy(x => x.FirstValue).ToList().FirstOrDefault();

then I'd agree - you are sorting your pairs and then taking the first. But LINQ is smarter than that, and you will not be sorting the set. You'll be taking the first from a potentially ordered but as yet unexecuted collection.


In addition to your point about Dictionary being unable to use a complex collection - say a list of Animal - how would you sort by an Animal? You can never sort by a complex object. You instead need to use the age of the animal as the key. Dictionary can do this very easily - in fact, the key of a Dictionary would never be the same as the value of a Dictionary, else what would be the point?

var animals = new List<Animal>();
// get some animals...

var animalictionary = animals.ToDictionary(a => a.Age);
// assuming the animals have distinct ages, else

var animalLookup = animals.ToLookup(a => a.Age);

foreach (var animalGroup in animalLookup)
{
    var age = animalGroup.Key;
    Console.WriteLine("All these animals are " + age);
    foreach (Animal animal in animalGroup)
    {
        Console.WriteLine(animal.name);
    }
} 
Kirk Broadhurst
  • 27,836
  • 16
  • 104
  • 169