I have a List. I want to print the longest address.
Pseudocode
foreach (var i in listAddr)
{
// access listAddr.Address.Length
// print longest address
// print shortest address
}
Very roughly, with no foreach:
var sortedAddr = listAddr.OrderBy(x => x.Address.Length);
var longestAddr = sortedAddr.Last();
var shortedAddr = sortedAddr.First();
As Jon said, this has O(n log n) complexity.
But could be reasonable if you don't have extreme performance need.
EDIT:
If you have a lot of same-length addresses you can do this:
var sortedGroups = listAddr.GroupBy(x => x.Address.Length).OrderBy(x => x.Key);
var longestAddresses = sortedGroups.Last();
var shortestAddresses = sortedGroups.First();
// just print iterating over longestAddresses and shortestAddresses ...
It sounds like you want MaxBy and MinBy functionality, e.g.
var maxEntry = listAddr.MaxBy(entry => entry.Address.Length);
Console.WriteLine(maxEntry.Address);
var minEntry = listAddr.MinBy(entry => entry.Address.Length);
Console.WriteLine(minEntry.Address);
Unfortunately there's nothing like this in plain LINQ to Objects, but we have an implementation in MoreLINQ and I believe Reactive Extensions has one in System.Interactive too.
Obviously you can sort by address size descending and then take the first result... that will be O(n log n) instead of O(n) complexity... that may well be fine in most cases. It feels inelegant to me though :)
Code from the MoreLINQ implementation of MaxBy (without comments :)
public static TSource MaxBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> selector)
{
return source.MaxBy(selector, Comparer<TKey>.Default);
}
public static TSource MaxBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
source.ThrowIfNull("source");
selector.ThrowIfNull("selector");
comparer.ThrowIfNull("comparer");
using (IEnumerator<TSource> sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence was empty");
}
TSource max = sourceIterator.Current;
TKey maxKey = selector(max);
while (sourceIterator.MoveNext())
{
TSource candidate = sourceIterator.Current;
TKey candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, maxKey) > 0)
{
max = candidate;
maxKey = candidateProjected;
}
}
return max;
}
}
}
This should work list.Max(x => x.Address) and list.Min(x => x.Address)
For ex, if you have a list like that
List<int> myList = new List<int>();
you are able to use myList.Max() and myList.Min() to get max and min values
Related
This question already has answers here:
Linq to Objects - return pairs of numbers from list of numbers
(12 answers)
Closed 7 years ago.
Using LINQ on an ordered set (array, list), is there a way to select or otherwise use two consecutive items? I am imagining the syntax:
list.SelectTwo((x, y) => ...)
Where x and y are the items at index i and i + 1 in the list/array.
There may be no way to do this, which I accept as a possibility, but I would at least like to say I tried to find an answer.
I am aware that I could use something other and LINQ to achieve this.
Thank you in advance.
Another answer presents a nice and clean solution using LINQ's Skip and Zip.
It is absolutely correct, but I'd like to point out that it enumerates the source twice. That may or may not matter, depending on each individual use case. If it matters for your case, here's a longer alternative that is functionally equivalent but enumerates the source once:
static class EnumerableUtilities
{
public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (selector == null) throw new ArgumentNullException(nameof(selector));
return SelectTwoImpl(source, selector);
}
private static IEnumerable<TResult> SelectTwoImpl<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
using (var iterator = source.GetEnumerator())
{
var item2 = default(TSource);
var i = 0;
while (iterator.MoveNext())
{
var item1 = item2;
item2 = iterator.Current;
i++;
if (i >= 2)
{
yield return selector(item1, item2);
}
}
}
}
}
Example:
var seq = new[] {"A", "B", "C", "D"}.SelectTwo((a, b) => a + b);
The resulting sequence contains "AB", "BC", "CD".
System.Linq.Enumerable.Zip combines two IEnumerables by pairing up the i-th element for each i. So you just need to Zip your list with a shifted version of it.
As a nice extension method:
using System.Collections.Generic;
using System.Linq;
static class ExtMethods
{
public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
return Enumerable.Zip(source, source.Skip(1), selector);
}
}
Example:
Enumerable.Range(1,5).SelectTwo((a,b) => $"({a},{b})");
Results in:
(1,2) (2,3) (3,4) (4,5)
You can do
list.Skip(i).Take(2)
This will return an IEnumerable<T> with only the two consecutive items.
I think you can select item and next item data in an ordered list like this:
var theList = new List<T>();
theList
.Select((item, index) => new { CurrIndex = index, item.Prop1, item.Prop2, theList[index + 1].Prop1 })
.Where(newItem => {some condition on the item});
However, index of the selected items should be less than list size - 1.
If the source sequence has an indexer, i.e. at minimum is IReadOnlyList<T> (array, list as mentioned in the question), and the idea is to split the sequence on consecutive pairs (which is not quite clear from the question), then it can be done simply like this
var pairs = Enumerable.Range(0, list.Count / 2)
.Select(i => Tuple.Create(list[2 * i], list[2 * i + 1]));
You can use a special overload of Select that allows you to use the index of the item, and the GroupBy method to split the list into groups. Each group would have two items. Here is an extension method that does that:
public static class ExtensionMethods
{
public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
return source.Select((item, index) => new {item, index})
.GroupBy(x => x.index/2)
.Select(g => g.Select(i => i.item).ToArray())
.Select(x => selector(x[0], x[1]));
}
}
And you can use it like this:
var list = new[] {1, 2, 3, 4, 5, 6};
var result = list.SelectTwo((x, y) => x + y).ToList();
This would return {3,7,11}
Please note that the above method groups the data in memory before starting to yield results. If you have large data sets, you might want to have a streaming approach (yield data as the data from the source is being enumerated), here is an example:
public static class ExtensionMethods
{
public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
bool first_item_got = false;
TSource first_item = default(TSource);
foreach (var item in source)
{
if (first_item_got)
{
yield return selector(first_item, item);
}
else
{
first_item = item;
}
first_item_got = !first_item_got;
}
}
}
I have the following records
The last 2 are children of record 3 and 4, I would like to be able to sort the records by amount but it should be that the non interest(parents) ones are sorted first then their children should show up after so for example it would be like this
2000
2000
20001
99.84 (child of the above)
50000
249.58 (child of the above)
Basically I would like my sort by amount to disregard the one with "IsInterest" set to true but make them show up after their parent.
I can do this by first taking all the parents into a new collection.. then go through the parent to see if there is any children then insert them after the parent in the new collection but I feel this is not efficient and dirty code so I thought I would ask maybe someone knows black magic.
The sort should also be aware of asc/desc on the amount.
I can post my code of ripping the collection apart and putting it together if it helps but I am trying not to use that code if possible.
My sort method takes a string for "ascending" or "descending" if that helps
Thank you
UPDATE2
I will point out that there is only ever going to be 2 levels, and that the children will ever only have one parent (no grand parents) and that each parent will have a maximum of 1 child
UPDATE code as requested (fields name may differ from the db fields..)
switch (sortMember.ToUpper())
{
case "AMOUNT":
{
//check to see if any imputed interests exist
if (contributions.Any(x => x.IsImputedInterest))
{
var children = contributions.Where(x => x.IsImputedInterest);
var sortedColl = contributions.Where(x => x.IsImputedInterest == false).OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING").ToList();
foreach (var child in children )
{
//find the parent
var parentIndex = sortedColl.FindIndex(x => x.ContributionId == child.ParentContirbutionId);
sortedColl.Insert(parentIndex+1, child);
}
}
else
{
contributions = contributions.OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING");
}
break;
}
}
.................
public static IOrderedEnumerable<TSource> OrderByWithDirection<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool descending)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
public static IOrderedQueryable<TSource> OrderByWithDirection<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, bool descending)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
Here's a single statement Linq solution:
var desc = order == "descending";
var result = list
//group parents with it's children
.GroupBy(x => x.ParentId ?? x.Id)
//move the parent to the first position in each group
.Select(g => g.OrderBy(x => x.ParentId.HasValue).ThenBy(x => desc ? -x.Amount : x.Amount))
//sort the groups by parents' amounts
.OrderBy(g => desc ? -g.First().Amount : g.First().Amount)
//retrieve the items from each group
.SelectMany(g => g);
Some performance hints:
You can drop the ThenBy(...) if there's always going to be at most one child or you don't care about children order
Use an if statement to check the order and have two versions of the statement - the second one using OrderByDescending/ThenByDescending, and drop the ternary operator (desc ? ... : ...) - otherwise it will be evaluated for each item
I'm not giving any guarantees on performance in relation to your current solution - it might as well turn out to be slower.
You can use the following generic method (not limited by levels or number of parent/children):
public static class Extensions
{
public static IEnumerable<T> ThenByHierarchy<T, TKey>(this IOrderedEnumerable<T> source, Func<T, TKey> keySelector, Func<T, TKey> parentKeySelector)
{
var itemByKey = source.ToDictionary(keySelector);
var processSet = new HashSet<T>();
var stack = new Stack<T>();
foreach (var item in itemByKey.Values)
{
for (var next = item; processSet.Add(next); )
{
stack.Push(next);
if (!itemByKey.TryGetValue(parentKeySelector(next), out next)) break;
}
while (stack.Count != 0)
yield return stack.Pop();
}
}
}
Just append it at the end of your OrderBy sequence like this
var result = contributions
.OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING")
.ThenByHierarchy(x => x.ContributionId, x => x.ParentContirbutionId);
UPDATE: It turns out that it's not so simple. While the method above provides a correct order for the leaf elements as well for the element to its parent, it does not order correctly the parents. The correct one is as follows (using another reusable method from here How to flatten tree via LINQ?, so if we don't count that it isn't really much bigger than the previous):
public static class Extensions
{
public static IEnumerable<T> HierarchicalOrder<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, Func<T, TKey> parentKeySelector, Func<IEnumerable<T>, IOrderedEnumerable<T>> order)
{
// Collect parent/child relation info
var itemById = source.ToDictionary(keySelector);
var childListById = new Dictionary<TKey, List<T>>();
var rootList = new List<T>();
foreach (var item in itemById.Values)
{
var parentKey = parentKeySelector(item);
List<T> childList;
if (parentKey == null || !itemById.ContainsKey(parentKey))
childList = rootList;
else if (!childListById.TryGetValue(parentKey, out childList))
childListById.Add(parentKey, childList = new List<T>());
childList.Add(item);
}
// Traverse the tree using in-order DFT and applying the sort on each sublist
return order(rootList).Expand(item =>
{
List<T> childList;
return childListById.TryGetValue(keySelector(item), out childList) ? order(childList) : null;
});
}
public static IEnumerable<T> Expand<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
{
var stack = new Stack<IEnumerator<T>>();
var e = source.GetEnumerator();
try
{
while (true)
{
while (e.MoveNext())
{
var item = e.Current;
yield return item;
var elements = elementSelector(item);
if (elements == null) continue;
stack.Push(e);
e = elements.GetEnumerator();
}
if (stack.Count == 0) break;
e.Dispose();
e = stack.Pop();
}
}
finally
{
e.Dispose();
while (stack.Count != 0) stack.Pop().Dispose();
}
}
}
and the usage in your case is simple
var result = contributions
.HierarchicalOrder(x => x.ContributionId, x => x.ParentContirbutionId, c =>
.OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING"));
I have the following: (C# code, VS2013)
class DailyTemp
{
public int Day;
public int LowTemp;
public int HighTemp;
}
List<DailyTemp> dailyTemps = new List<DailyTemp>();
//I fill this list with DailyTemp objects
My questions is, how do I write a lambda expression to go through my list and return the day that has the smallest difference between high and low temps? Thank you for your time.
Try:
return dailyTemps.OrderBy(dt => dt.HighTemp - dt.LowTemp).Select(dt => dt.Day).First();
As noted by #Colin DeClue, it's more efficient to call Select before First so that we're retrieving the minimum needed information from the data source (which can matter on larger data sets coming from a DB).
If you want to do an approach with a single pass over the sequence and using built-in methods, you can leverage Enumerable.Aggregate and keep the argument with the lowest temperature difference, discarding the other.
Func<DailyTemp, int> tempDiff = x => x.HighTemp - x.LowTemp;
var day = dailyTemps.Aggregate((a, b) => tempDiff(a) < tempDiff(b) ? a : b);
Otherwise, MoreLinq's MinBy might read simpler (externally available API), as well as the OrderBy method available within the BCL (that requires sorting), each approach being highlighted in other visible answers.
We can use the MinBy method of MoreLINQ (argument validation removed below) to get the min value of a collection based on the value of a selector:
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector)
{
return source.MinBy(selector, Comparer<TKey>.Default);
}
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
using (IEnumerator<TSource> sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence was empty");
}
TSource min = sourceIterator.Current;
TKey minKey = selector(min);
while (sourceIterator.MoveNext())
{
TSource candidate = sourceIterator.Current;
TKey candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, minKey) < 0)
{
min = candidate;
minKey = candidateProjected;
}
}
return min;
}
}
This lets us write:
var day = dailyTemps.MinBy(dt => dt.HighTemp - dt.LowTemp);
I'm writing an entry for an AI competition in C#, and I'm looking for a more elegant way to search for items. (I'm much more familiar with embedded C programming, but I prefer C# for an AI contest.)
The contest server is using dmcs to compile entries, which is .Net framework 4.0; I'm using Visual Studio Express 2013 for my testing.
I'm trying to search for an item in a list with the maximum value of a parameter that also meets a certain prerequisite. I don't want the maximum value, though, I want the item that has said maximum value.
Here's my original code that does what I want using a foreach loop:
List<Region> myList = new List<Region>();
// ...
// myList gets populated with elements
// ...
Region biggest = null;
int biggestSize = -1;
foreach (Region r in myList)
{
// We only want elements that are eligible for expansion
if (r.EligibleForExpansion())
{
if (r.Size > biggestSize)
{
biggest = r;
biggestSize = r.Size;
}
}
}
return biggest; // I want the biggest Region, not the Size of the biggest region.
I'm trying to find a more elegant way to do this so I don't have foreach loops all over my code. I tried this:
return myList.Max(delegate(Region r) { if (r.EligibleForExpansion()) return r.Size; else return -1; });
However, that returns the Size value of the largest region, not the largest Region itself (which is what I need).
I know that my foreach code will return null if no Region meets the requirement while the Max code will give -1 (or any Region that doesn't meet the requirement); I can deal with either way.
I don't think I can just make Region IComparable, though; I have many searches for Region objects, and I need to sort by different parameters at different times, so the comparison function would be different in different searches.
I could just wrap my foreach code in a static function and call that wherever I need to search, but it seems like there should be a more elegant way to do this in C#.
Use MaxBy from moreLINQ library:
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector)
{
return source.MaxBy(selector, Comparer<TKey>.Default);
}
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
if (comparer == null) throw new ArgumentNullException("comparer");
using (var sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence contains no elements");
}
var max = sourceIterator.Current;
var maxKey = selector(max);
while (sourceIterator.MoveNext())
{
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, maxKey) > 0)
{
max = candidate;
maxKey = candidateProjected;
}
}
return max;
}
}
like that:
var item = myList.Where(x => x.EligibleForExpansion())
.MaxBy(x => x.Size);
How about this?
myList.Where(r => r.EligibleForExpansion).OrderBy(r => r.Size).LastOrDefault()
You can use Aggregate out of the box for this purpose:
var item = myList
.Where(r => r.EligibleForExpansion())
.Aggregate((Region)null, (max, cur) => (max == null ? cur : cur.Size > max.Size ? cur : max));
If Region were a value type (which it isn't) you could wrap the initial value in a nullable, and get a null value for an empty list:
var item = myList
.Where(r => r.EligibleForExpansion())
.Aggregate((Region?)null, (max, cur) => (max == null ? cur : cur.Size > max.Value.Size ? cur : max));
I need to get a Kvp from a list of List<KeyValuePair<Int, Int>> depending on the minimum value.
I have tried this:
KeyValuePair<Int, Int> kvp= listOfKvps.Min(e=> e.Key);
but this return only the value, not the whole KeyValuePair which I need.
var min = listOfKvps.OrderBy(kvp => kvp.Key).First();
If you want to do it with a single O(n) pass through the sequence, rather than requiring an O(n log n) ordering, then you could do it like this:
var min = listOfKvps.Aggregate((agg, kvp) => (kvp.Key < agg.Key) ? kvp : agg);
(Of course, the second version is much less readable/intuitive than the first, even if it does have better theoretical performance. It would make more sense to use some sort of MinBy method: either write your own, use the version from Marc's answer or use the version from MoreLINQ.)
There is no inbuilt MinBy method, so you could either write a MinBy extension method, or just .OrderBy(x => x.Key).First(). A MinBy would be O(n) so would be more efficient - but more code to write ;p
For example, you could use:
var kvp= listOfKvps.MinBy(e=> e.Key);
with:
public static class SomeUtil {
public static TSource MinBy<TSource, TValue>(
this IEnumerable<TSource> source, Func<TSource, TValue> selector) {
using (var iter = source.GetEnumerator())
{
if (!iter.MoveNext()) throw new InvalidOperationException("no data");
var comparer = Comparer<TValue>.Default;
var minItem = iter.Current;
var minValue = selector(minItem);
while (iter.MoveNext())
{
var item = iter.Current;
var value = selector(item);
if (comparer.Compare(minValue, value) > 0)
{
minItem = item;
minValue = value;
}
}
return minItem;
}
}
}
I would suggest you use the MinBy extension-method from MoreLinq.
Alternatively:
var minKey = listOfKvps.Min(kvp => kvp.Key);
var minKvp = listOfKvps.First(kvp => kvp.Key == minKey);
This is still O(n), although it requires 2 passes over the list. Sorting the list and then picking the first element is more terse, but is O(n * logn), which may be relevant for larger lists.