Sort list by field (C#) - c#

I have class like:
class SortNode
{
public Int32 m_valRating = 0;
public SortNode(Int32 valRating)
{
this.m_valRating = valRating;
}
}
and some list refSortNodeList:
List<SortNode> refSortNodeList = new List<SortNode>();
Random refRandom = new Random();
for (int i = 0; i < 100; ++i)
{
refSortNodeList.Add(new SortNode(refRandom.Next(-10, 30)));
}
foreach (var varSortNode in refSortNodeList)
{
Console.WriteLine("SortNode rating is {0}", varSortNode.m_valRating);
}
How to sort easily my refSortNodeList by m_valRating field? Or maybe I need to use some another List class?

list.Sort((x,y) =>
x.m_valRating.CompareTo(y.m_valRating));

In-place:
refSortNodeList.Sort(
(x, y) =>
x == null ? (y == null ? 0 : -1)
: (y == null ? 1 : x.m_valRating.CompareTo(y.m_valRating))
);
Creating a new enumeration:
var newEnum = refSortNodeList.OrderBy(x => x.m_valRating);
Creating a new list:
var newList = refSortNodeList.OrderBy(x => x.m_valRating).ToList();
In-place is fastest and most memory efficient, but no good if you want to also retain the old list.
The next is faster than the last and gives results as they go, but you have to re-do the sort to use it again, in which case the third is the one to go for.

Use Linq order by.
var mySortedList = refSortNodeList.OrderBy(x => x.m_valRating);
Here is a real live example where I am pulling a list from a database but it is exactly the same concept.
vendorProducts = (from vp in db.COMPANIES_VND_PRODUCTS
join p in db.CT_CT_INV_CLASSES on vp.CLASS_ID equals p.CLASS_ID
join m in db.CT_CT_MODALITY_CODES on vp.MODALITY_ID equals m.MODALITY_ID
where vp.COMPANY_ID == companyId
select new ProductTypeModality
{
Active = p.ACTIVE.Equals("Y") ? true : false,
BioMedImaging = p.BIOMED_IMAGING,
Code = p.CLASS_CODE,
Description = p.DESCRIPTION,
Id = p.CLASS_ID,
PricingMargin = p.PRICING_MARGIN,
ModalityCode = m.MODALITY_CODE,
ModalityId = m.MODALITY_ID,
VendorId = companyId
}).OrderBy(x => x.Code).ToList<ProductTypeModality>();

Implement IComparable<T>

You can use Linq for basic sorts:
refSortNodeList.OrderBy(n => n.m_valRating);
If you need more complex sorting your will need to implement IComparable to use the built in sorting.

Try this:
refSortNodeList.Sort(new delgate(SortNode x, SortNode y)
{
return x.CompareTo(y);
}
);

It's easy using linq:
var newlist = refSortNodeList.sort( n => n.m_valRating );

List<SortNode> refSortNodeList = new List<SortNode> ();
Random refRandom = new Random ();
for (int i = 0; i < 100; ++i) {
refSortNodeList.Add (new SortNode (refRandom.Next (-10, 30)));
}
// Use this (Linq) if you're using .NET 3.5 or above.
var sortedList = refSortNodeList.OrderBy (node => node.m_valRating);
foreach (var varSortNode in sortedList) {
Console.WriteLine ("SortNode rating is {0}", varSortNode.m_valRating);
}
// Use this otherwise (e.g. .NET 2.0)
refSortNodeList.Sort (
delegate (SortNode n1, SortNode n2) {
return n1.m_valRating.CompareTo (n2.m_valRating);
}
);
foreach (var varSortNode in refSortNodeList) {
Console.WriteLine ("SortNode rating is {0}", varSortNode.m_valRating);
}

Related

Mapping a list of ints into a list of MinMax ranges with LINQ [duplicate]

When i have a list
IList<int> list = new List<int>();
list.Add(100);
list.Add(200);
list.Add(300);
list.Add(400);
list.Add(500);
What is the way to extract a pairs
Example : List elements {100,200,300,400,500}
Expected Pair : { {100,200} ,{200,300} ,{300,400} ,{400,500} }
The most elegant way with LINQ: list.Zip(list.Skip(1), Tuple.Create)
A real-life example: This extension method takes a collection of points (Vector2) and produces a collection of lines (PathSegment) needed to 'join the dots'.
static IEnumerable<PathSegment> JoinTheDots(this IEnumerable<Vector2> dots)
{
var segments = dots.Zip(dots.Skip(1), (a,b) => new PathSegment(a, b));
return segments;
}
This will give you an array of anonymous "pair" objects with A and B properties corresponding to the pair elements.
var pairs = list.Where( (e,i) => i < list.Count - 1 )
.Select( (e,i) => new { A = e, B = list[i+1] } );
You can use a for loop:
var pairs = new List<int[]>();
for(int i = 0; i < list.Length - 1; i++)
pairs.Add(new [] {list[i], list[i + 1]);
You can also use LINQ, but it's uglier:
var pairs = list.Take(list.Count - 1).Select((n, i) => new [] { n, list[i + 1] });
EDIT: You can even do it on a raw IEnumerable, but it's much uglier:
var count = list.Count();
var pairs = list
.SelectMany((n, i) => new [] { new { Index = i - 1, Value = n }, new { Index = i, Value = n } })
.Where(ivp => ivp.Index >= 0 && ivp.Index < count - 1) //We only want one copy of the first and last value
.GroupBy(ivp => ivp.Index, (i, ivps) => ivps.Select(ivp => ivp.Value));
More general would be:
public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> values, int count, Func<TSource[], TResult> pairCreator)
{
if (count < 1) throw new ArgumentOutOfRangeException("count");
if (values == null) throw new ArgumentNullException("values");
if (pairCreator == null) throw new ArgumentNullException("pairCreator");
int c = 0;
var data = new TSource[count];
foreach (var item in values)
{
if (c < count)
data[c++] = item;
if (c == count)
{
yield return pairCreator(data);
c = 0;
}
}
}
Following solution uses zip method. Zip originalList and originalList.Skip(1) so that one gets desired result.
var adjacents =
originalList.Zip(originalList.Skip(1),
(a,b) => new {N1 = a, N2 = b});
Using .Windowed() from MoreLINQ:
var source = new[] {100,200,300,400,500};
var result = source.Windowed(2).Select(x => Tuple.Create(x.First(),x.Last()));
Off the top of my head and completely untested:
public static T Pairwise<T>(this IEnumerable<T> list)
{
T last;
bool firstTime = true;
foreach(var item in list)
{
if(!firstTime)
return(Tuple.New(last, item));
else
firstTime = false;
last = item;
}
}

How to take last records from list

I have list as follows
static List<MessageDetail> CurrentMessage = new List<MessageDetail>();
Dynamically, values assigned to this list for example:
CurrentMessage.Add(new MessageDetail { UserName = 123,GroupName = somegrp, Message = somemsg });
Here, I want to take last 5 or so records.
// this returns first 5 result, dont want to user orderby clause either
CurrentMessagesForGroup = CurrentMessage
.Where(c => c.GroupName == groupName)
.Take(5).ToList();
Is there a way to implement TakeLast() attribute? Or any kind of help will be appreciated. Thanks for your time.
Use skip:
CurrentMessagesForGroup = CurrentMessage
.Where(c => c.GroupName == groupName).Skip(Math.Max(0, CurrentMessage.Count() - 5)).ToList();
EDIT: I also find this that I think it is more easier to use (MoreLinq):
using MoreLinq;
var CurrentMessagesForGroup2 = CurrentMessage.TakeLast(5);
Use an OrderBy (ASC or DESC) to get the records lined up correctly for your Take operation.
Ascending:
CurrentMessagesForGroup = CurrentMessage
.Where(c => c.GroupName == groupName)
.OrderBy(c => c.GroupName)
.Take(5)
.ToList();
or Descending:
CurrentMessagesForGroup = CurrentMessage
.Where(c => c.GroupName == groupName)
.OrderByDescending(c => c.GroupName)
.Take(5)
.ToList();
If anyone using DotNet Core 2 or above or DotNet Standard 2.1 or above then you can use Linq's built in .TakeLast()
Reference: Microsoft Documentation here
You could use Reverse(), which is slightly perverse.
CurrentMessagesForGroup = CurrentMessage
.Where(c => c.GroupName == groupName)
.Reverse()
.Take(5).ToList();
I use an extension method for this.
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int numElements)
{
return source.Skip(Math.Max(0, source.Count() - numElements));
}
And to use it:
CurrentMessagesForGroup = CurrentMessage.Where(c => c.GroupName == groupName).TakeLast(5).ToList();
Edit: Credit to Using Linq to get the last N elements of a collection?
I like this implementation, it uses a circular buffer.
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> input, int n)
{
if (n == 0)
yield break;
int tail = 0;
int head = 0;
int count = 0;
T[] buffer = new T[n];
foreach (T item in input)
{
buffer[tail] = item;
tail = (tail + 1) % n;
if (count < n)
count++;
else
head = (head + 1) % n;
}
for (int i = 0; i < count; i++)
yield return buffer[(head + i) % n];
}
If the MessageDetails class has numeric Id or Created date time we can use
var lastRecords= CurrentMessage.OrderByDescending(i=>i.Id).Where(p=>p.GroupName==groupName).Take(5).ToList();

group by multiple columns (Dynamically) of datatable by linq Query

i want to group by multiple columns in a datatable by linq query.
i tried like this,
var _result = from row in tbl.AsEnumerable()
group row by new
{
id=row.Field<object>(_strMapColumn),
value=row.Field<object>(_strValueColumn),
} into g
select new
{
_strMapColumn = g.Key.id,
ToolTip = g.Sum(r => grp.Sum(r => r.Field<Double>(__strToolTip[1]))),
};
its works fine. my question is i have 10 column names in a strToolTip array i want to access 10 column names dynamically like for loop is it possible?
i want like this
select new
{_strMapColumn = g.Key.id,
for(int index = 1; index <= 10; index++)
{
ToolTip+index = g.Sum(r => getDoubleValue(r.Field<Double>(__strToolTip[1])))
}
};
and also want to add a DataType Dynamically please kindly provide the answer for solve this.
linq query is new for me.
You could group by a Dictionary and pass a custom comparer:
public class MyComparer : IEqualityComparer<Dictionary<string, object>> {
public bool Equals(Dictionary<string, object> a, Dictionary<string, object> b) {
if (a == b) { return true; }
if (a == null || b == null || a.Count != b.Count) { return false; }
return !a.Except(b).Any();
}
}
IEnumerable<string> columnsToGroupBy = ...
var rows = tbl.AsEnumerable();
var grouped = rows.GroupBy(r => columnsToGroupBy.ToDictionary(c => c, c => r[c]), new MyComparer());
var result = grouped.Select(g => {
// whatever logic you want with each grouping
var id = g.Key["id"];
var sum = g.Sum(r => r.Field<int>("someCol"));
});
Thanks to ChaseMedallion, I got dynamic grouping working.
Equals method was not enough, I had to add GetHashCode to MyComparer as well:
public int GetHashCode(Dictionary<string, object> a)
{
return a.ToString().ToLower().GetHashCode();
}

How can I use LINQ to calculate the longest streak?

Currently, this is just something I am curious about, I don't have any code I am working on but I am wondering how this could be achieved...
Lets say for example that I have an application that tracks the results of all the football teams in the world. What I want to be able to do is to identify the longest "win" streak for any given team.
I imagine I would most likely have some sort of data table like so:
MatchDate datetime
TeamA string
TeamB string
TeamAGoals int
TeamBGoals int
So what I would want to do for example is find the longest win streak where TeamA = "My Team" and obviously this would mean TeamAGoals must be greater than TeamBGoals.
As I have said, this is all just for example. It may be better for a different DB design for something like this. But the root question is how to calculate the longest streak/run of matching results.
This is an old question now, but I just had to solve the same problem myself, and thought people might be interested in a fully LINQ implementation of Rawling's LongestStreak extension method. This uses Aggregate with a seed and result selector to run through the list.
public static int LongestStreak<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
return source.Aggregate(
new {Longest = 0, Current = 0},
(agg, element) => predicate(element) ?
new {Longest = Math.Max(agg.Longest, agg.Current + 1), Current = agg.Current + 1} :
new {agg.Longest, Current = 0},
agg => agg.Longest);
}
There's no out-of-the-box LINQ method to count streaks, so you'll need a custom LINQy method such as
public static int LongestStreak<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
int longestStreak = 0;
int currentStreak = 0;
foreach (TSource s in source)
{
if (predicate(s))
currentStreak++;
else
{
if (currentStreak > longestStreak) longestStreak = currentStreak;
currentStreak = 0;
}
}
if (currentStreak > longestStreak) longestStreak = currentStreak;
return longestStreak;
}
Then, to use this, first turn each "match result" into a pair of "team results".
var teamResults = matches.SelectMany(m => new[] {
new {
MatchDate = m.MatchDate,
Team = m.TeamA,
Won = m.TeamAGoals > m.TeamBGoals },
new {
MatchDate = m.MatchDate,
Team = m.TeamB,
Won = m.TeamBGoals > m.TeamAGoals }
});
Group these by team.
var groupedResults = teamResults.GroupBy(r => r.Team);
Then calculate the streaks.
var streaks = groupedResults.Select(g => new
{
Team = g.Key,
StreakLength = g
// unnecessary if the matches were ordered originally
.OrderBy(r => r.MatchDate)
.LongestStreak(r => r.Won)
});
If you want the longest streak only, use MoreLinq's MaxBy; if you want them all ordered, you can use OrderByDescending(s => s.StreakLength).
Alternatively, if you want to do this in one pass, and assuming matches is already ordered, using the following class
class StreakAggregator<TKey>
{
public Dictionary<TKey, int> Best = new Dictionary<TKey, int>();
public Dictionary<TKey, int> Current = new Dictionary<TKey, int>();
public StreakAggregator<TKey> UpdateWith(TKey key, bool success)
{
int c = 0;
Current.TryGetValue(key, out c);
if (success)
{
Current[key] = c + 1;
}
else
{
int b = 0;
Best.TryGetValue(key, out b);
if (c > b)
{
Best[key] = c;
}
Current[key] = 0;
}
return this;
}
public StreakAggregator<TKey> Finalise()
{
foreach (TKey k in Current.Keys.ToArray())
{
UpdateWith(k, false);
}
return this;
}
}
you can then do
var streaks = teamResults.Aggregate(
new StreakAggregator<string>(),
(a, r) => a.UpdateWith(r.Team, r.Won),
(a) => a.Finalise().Best.Select(kvp =>
new { Team = kvp.Key, StreakLength = kvp.Value }));
and OrderBy or whatever as before.
You can get all results of team with single query:
var results = from m in Matches
let homeMatch = m.TeamA == teamName
let awayMatch = m.TeamB == teamName
let hasWon = (homeMatch && m.TeamAGoals > m.TeamBGoals) ||
(awayMatch && m.TeamBGoals > m.TeamAGoals)
where homeMatch || awayMatch
orderby m.MatchDate
select hasWon;
Then just do simple calculation of longest streak:
int longestStreak = 0;
int currentStreak = 0;
foreach (var hasWon in results)
{
if (hasWon)
{
currentStreak++;
if (currentStreak > longestStreak)
longestStreak = currentStreak;
continue;
}
currentStreak = 0;
}
You can use it as is, extract to method, or create IEnumerable extension for calculating longest sequence in results.
You could make use of string.Split. Something like this:
int longestStreak =
string.Concat(results.Select(r => (r.ours > r.theirs) ? "1" : "0"))
.Split(new[] { '0' })
.Max(s => s.Length);
Or, better, create a Split extension method for IEnumerable<T> to avoid the need to go via a string, like this:
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, Predicate<T> p)
{
while (true)
{
items = items.SkipWhile(i => !p(i));
var trueItems = items.TakeWhile (i => p(i)).ToList();
if (trueItems.Count > 0)
{
yield return trueItems;
items = items.Skip(trueItems.Count);
}
else
{
break;
}
}
}
You can then simply do this:
int longestStreak = results.Split(r => r.ours > r.theirs).Max(g => g.Count());

Get sum of two columns in one LINQ query

let's say that I have a table called
Items (ID int, Done int, Total int)
I can do it by two queries:
int total = m.Items.Sum(p=>p.Total)
int done = m.Items.Sum(p=>p.Done)
But I'd like to do it in one query, something like this:
var x = from p in m.Items select new { Sum(p.Total), Sum(p.Done)};
Surely there is a way to call aggregate functions from LINQ syntax...?
This will do the trick:
LINQ Query Syntax:
from p in m.Items
group p by 1 into g
select new
{
SumTotal = g.Sum(x => x.Total),
SumDone = g.Sum(x => x.Done)
};
LINQ Method Syntax:
m.Items
.GroupBy(r => 1)
.Select(g => new
{
SumTotal = g.Sum(x => x.Total),
SumDone = g.Sum(x => x.Done)
});
Notice that this solution (both syntax) returns a list of 1 item, so you might want to add at the end:
.FirstOrDefault();
To sum the table, group by a constant:
from p in m.Items
group p by 1 into g
select new {
SumTotal = g.Sum(x => x.Total),
SumDone = g.Sum(x => x.Done)
}
How about
m.Items.Select(item => new { Total = item.Total, Done = item.Done })
.Aggregate((t1, t2) => new { Total = t1.Total + t2.Total, Done = t1.Done + t2.Done });
Figuring out where to extract the sums or other aggregate in the rest of my code confused me, until I remembered that the variable I constructed was an Iqueryable. Suppose we have a table in our database composed of Orders, and we want to produce a summary for the ABC company:
var myResult = from g in dbcontext.Ordertable
group p by (p.CUSTNAME == "ABC") into q // i.e., all of ABC company at once
select new
{
tempPrice = q.Sum( x => (x.PRICE ?? 0m) ), // (?? makes sure we don't get back a nullable)
tempQty = q.Sum( x => (x.QTY ?? 0m) )
};
Now the fun part -- tempPrice and tempQty aren't declared anywhere but they must be part of myResult, no? Access them as follows:
Console.Writeline(string.Format("You ordered {0} for a total price of {1:C}",
myResult.Single().tempQty,
myResult.Single().tempPrice ));
A number of other Queryable methods could be used as well.
With a helper tuple class, either your own or—in .NET 4—the standard ones you can do this:
var init = Tuple.Create(0, 0);
var res = m.Items.Aggregate(init, (t,v) => Tuple.Create(t.Item1 + v.Total, t.Item2 + v.Done));
And res.Item1 is the total of the Total column and res.Item2 of the Done column.
This has been answered already, but the other answers will still do multiple iterations over the collection (multiple calls to Sum) or create lots of intermediate objects/Tuples which may be fine, but if it isn't, then you can create an extension method (or multiple) that does it the old-fashioned way but fits well in a LINQ expression.
Such an extension method would look like this:
public static Tuple<int, int> Sum<T>(this IEnumerable<T> collection, Func<T, int> selector1, Func<T, int> selector2)
{
int a = 0;
int b = 0;
foreach(var i in collection)
{
a += selector1(i);
b += selector2(i);
}
return Tuple.Create(a, b);
}
And you can use it like this:
public class Stuff
{
public int X;
public int Y;
}
//...
var stuffs = new List<Stuff>()
{
new Stuff { X = 1, Y = 10 },
new Stuff { X = 1, Y = 10 }
};
var sums = stuffs.Sum(s => s.X, s => s.Y);
//Calculate the total in list field values
//Use the header file:
Using System.Linq;
int i = Total.Sum(G => G.First);
//By using LINQ to calculate the total in a list field,
var T = (from t in Total group t by Total into g select g.Sum(t => t.First)).ToList();
//Here Total is a List and First is the one of the integer field in list(Total)
Using the language support for tuples introduced in C# 7.0 you can solve this using the following LINQ expression:
var itemSums = m.Items.Aggregate((Total: 0, Done: 0), (sums, item) => (sums.Total + item.Total, sums.Done + item.Done));
Full code sample:
var m = new
{
Items = new[]
{
new { Total = 10, Done = 1 },
new { Total = 10, Done = 1 },
new { Total = 10, Done = 1 },
new { Total = 10, Done = 1 },
new { Total = 10, Done = 1 },
},
};
var itemSums = m.Items.Aggregate((Total: 0, Done: 0), (sums, item) => (sums.Total + item.Total, sums.Done + item.Done));
Console.WriteLine($"Sum of Total: {itemSums.Total}, Sum of Done: {itemSums.Done}");
When you use group by Linq creates a new collection of items so you have two collections of items.
Here's a solution to both problems:
summing any amount of members in one iteration and
avoid duplicating your item's collection
Code:
public static class LinqExtensions
{
/// <summary>
/// Computes the sum of the sequence of System.Double values that are obtained
/// by invoking one or more transform functions on each element of the input sequence.
/// </summary>
/// <param name="source">A sequence of values that are used to calculate a sum.</param>
/// <param name="selectors">The transform functions to apply to each element.</param>
public static double[] SumMany<TSource>(this IEnumerable<TSource> source, params Func<TSource, double>[] selectors)
{
if (selectors.Length == 0)
{
return null;
}
else
{
double[] result = new double[selectors.Length];
foreach (var item in source)
{
for (int i = 0; i < selectors.Length; i++)
{
result[i] += selectors[i](item);
}
}
return result;
}
}
/// <summary>
/// Computes the sum of the sequence of System.Decimal values that are obtained
/// by invoking one or more transform functions on each element of the input sequence.
/// </summary>
/// <param name="source">A sequence of values that are used to calculate a sum.</param>
/// <param name="selectors">The transform functions to apply to each element.</param>
public static double?[] SumMany<TSource>(this IEnumerable<TSource> source, params Func<TSource, double?>[] selectors)
{
if (selectors.Length == 0)
{
return null;
}
else
{
double?[] result = new double?[selectors.Length];
for (int i = 0; i < selectors.Length; i++)
{
result[i] = 0;
}
foreach (var item in source)
{
for (int i = 0; i < selectors.Length; i++)
{
double? value = selectors[i](item);
if (value != null)
{
result[i] += value;
}
}
}
return result;
}
}
}
Here's the way you have to do the summation:
double[] result = m.Items.SumMany(p => p.Total, q => q.Done);
Here's a general example:
struct MyStruct
{
public double x;
public double y;
}
MyStruct[] ms = new MyStruct[2];
ms[0] = new MyStruct() { x = 3, y = 5 };
ms[1] = new MyStruct() { x = 4, y = 6 };
// sum both x and y members in one iteration without duplicating the array "ms" by GROUPing it
double[] result = ms.SumMany(a => a.x, b => b.y);
as you can see
result[0] = 7
result[1] = 11

Categories

Resources