Reputation: 2054
I'm working with an IEnumerable Dictionary (data.Tags)
inside a List (masterList)
.
Here is a typical query I'm doing (which works):
var tagList =
(from data in masterList
from tag in data.Tags
where tag.id == 0x10
select tag).Distinct(new TagComparer());
The tag
class has fields Id
, Value
and TranslatedValue
. I want to search based on Id
, use Value
to determine which is the minimum and then return the TranslatedValue
(instead of Value
).
All of my attempts thus far throw an ArgumentException, such as this:
var tagList =
(from data in masterList
from tag in data.Tags
where tag.id == 0x10
select new
{
tag.Value,
tag.TranslatedValue
};
return tagList.Min().TranslatedValue;
Is there an elegant solution to this?
Upvotes: 0
Views: 764
Reputation: 11592
You could implement IComparable<T>
for your tag class, and base the comparison on the Value
property. Then instead of selecting the minimum Value
you can select the minimum tag and take the TranslatedValue
from that.
Some code to demonstrate what I mean:
class tag : IComparable<tag>
{
public int Id { get; set; }
public int Value { get; set; }
public int TranslatedValue { get; set; }
public int CompareTo(tag other)
{
if (other.Value > this.Value)
return -1;
if (other.Value < this.Value)
return 1;
return 0;
}
public override string ToString()
{
return string.Format("Id:{0}, Value: {1}, TranslatedValue: {2}", Id, Value, TranslatedValue);
}
}
That set your tag class up to implement IComparable<T>
. To use it to get the TranslatedValue
it's as simple as:
int minTranslatedValue = tags.Min().TranslatedValue;
Where tags
is some sort of IEnumerable<Tag>
collection.
Upvotes: 1
Reputation: 71565
My answer when I encountered this exact problem before was a little more involved than the others here, but it works very well in these situations and is a little more performant; develop an ObjectWithMin() extension method that will return the entire object that contains the minimum value of a specified projection. Here's a basic algorithm; it iterates the enumerable only once and executes linearly instead of in nlogn-time:
public static T ObjectWithMin<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
where TResult : IComparable<TResult>
{
if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");
var seed = elements.Select(t => new { Object = t, Projection = projection(t) }).First();
return elements.Aggregate(seed,
(s, x) =>
projection(x).CompareTo(s.Projection) < 0
? new {Object = x, Projection = projection(x)}
: s
).Object;
}
Usage:
var tagList =
(from data in masterList
from tag in data.Tags
where tag.id == 0x10
select new
{
tag.Value,
tag.TranslatedValue
};
return tagList.ObjectWithMin(x=>x.Value).TranslatedValue;
Upvotes: 1
Reputation: 700232
You should be able to sort by one property, take the first item, and get another property:
return
masterList
.SelectMany(data => data.Tags)
.Where(tag => tag.id == 0x10)
.OrderBy(tag => tag.Value)
.First()
.TranslatedValue;
Upvotes: 0
Reputation: 245399
You can use OrderBy
to order the elements in ascending order (putting the minimum value at the first position), Select
to get your translated value, and then First
to take that minimum element:
var minVal = (from data in masterList
from tag in data.Tags
where tag.id == 0x10
select tag)
.Distinct(new TagComparer())
.OrderBy(t => t.Value) // lowest value will be first in the list
.First() // take the first element, which is the min
.TranslatedValue
Upvotes: 3