Order IGrouping in C# - c#

Example is here, should work in online compilers:
internal class Program
{
static void Main(string[] args)
{
var i1 = new Item();
i1.Val1 = 1;
i1.Val2 = 2.1;
var i2 = new Item();
i2.Val1 = 1;
i2.Val2 = 1.5;
var i3 = new Item();
i3.Val1 = 3;
i3.Val2 = 0.3;
var list = new List<Item>
{
i1,
i2,
i3
};
var grouped = list.GroupBy(x => x.Val1);
Program p = new Program();
foreach(var group in grouped)
p.Func(group);
}
public void Func(IGrouping<int, Item> list)
{
list.OrderBy(x => x.Val2); //list will be ordered, but not saved
list = (IGrouping<int, Item>)list.OrderBy(x => x.Val2); //exception
}
}
public class Item
{
public int Val1 { get; set; }
public double Val2 { get; set; }
}
It's simplified code of what I'm trying to do - I need to order list inside Func, but I have no idea how. First line works in theory, but since it's not a void it's not working in practice - list is not actually ordered.
Second line should work, actually Visual Studio suggested that, but it throws runtime exception - Unable to cast object of type System.Linq.OrderedEnumerable to System.Linq.IGrouping.
I'm out of ideas for the time being, but there is no way of bypassing it - I absolutely need to order it there.
Edit
My current solution is to use Select(x => x) to flatten the IGrouping to normal List, this way I can easily order it and edit values without losing reference to grouped. If you really want to keep IGrouping then you are out of luck, does not seem to be possible.

Try this.
var grouped = list.GroupBy(x => x.Val1).Select(a=> a.OrderBy(a=>a.Val2).ToList());
OrderBy returns IOrderedEnumerable you can't cast that to IGrouping

Use First method at the end in order to get IGrouping collection of ordered items.
public void Func(IGrouping<int, Item> list)
{
list = list.OrderBy(x => x.Val2).GroupBy(x => x.Val1).First();
}

Your example code doesn't show what you are trying to arrive at.
list.OrderBy(x => x.Val2); //list will be ordered, but not saved
OrderBy doesn't order the existing collection in-place. It effectively returns a new collection.
list = (IGrouping<int, Item>)list.OrderBy(x => x.Val2); //exception
OrderBy returns an IOrderedEnumerable<TElement>. Both IOrderedEnumerable<TElement> and IGrouping<TKey,TElement> derive from IEnumerable<TElement> but you can't cast an IOrderedEnumerable to an IGrouping.
If all you want is to write out the values, then Func could be:
public IEnumerable<Item> Func(IGrouping<int, Item> list)
{
return list.OrderBy(x => x.Val2);
}
and the foreach loop could be:
foreach(var group in grouped)
{
var orderedList = p.Func(group);
Console.WriteLine($"group: {group.Key}");
foreach (var value in orderedList)
{
Console.WriteLine($" {value.Val2}");
}
}
Hopefully this helps.

Related

Separate collection on 2 different NEW collections

I have collection of elements and one additional small collection as filter.
I need to separate it on 2 new collections by some filter. In my case it is first collection that contains some elements and another that doesn't.
There aren't items that doesn't exists out of that 2 new collections.
I did it like :
var collection1= baseCollection.Where(r => filterCollection.Contains(r.Property)).ToList();
var collection2= baseCollection.Where(r => !filterCollection.Contains(r.Property)).ToList();
But is there another, I hope more elegant way, to separate collection?
For me it looks like "I repeat myself", use almost the same code 2 times.
You can create a variable for the function - this way you will not "repeat yourself" (wouldn't use in this case, there are better options below, but still an option):
Func<YourClass,bool> filtering = (r) => filterCollection.Contains(r.Property);
var collection1 = baseCollection.Where(r => filtering(r));
var collection2 = baseCollection.Where(r => !filtering(r));
If your type of the collection overrides Equals and GetHashCode you can use Except:
var collection1 = baseCollection.Where(r => filterCollection.Contains(r.Property));
var collection2 = baseCollection.Except(collection1);
Using Except with a given IEqualityComparer (Check also first comment for guidlines):
public class Comparer : IEqualityComparer<YourClass>
{
public bool Equals(YourClass x, YourClass y)
{
// Your implementation
}
public int GetHashCode(YourClass obj)
{
// Your implementation
}
}
var collection1 = baseCollection.Where(r => filterCollection.Contains(r.Property));
var collection2 = baseCollection.Except(collection1, new Comparer());
You can also use GroupBy (probably less good performance wise):
var result baseCollection.GroupBy(r => filterCollection.Contains(r.Property))
.ToDictionary(key => key.Key, value => value.ToList());
var collection1 = result[true];
var collection2 = result[false];
Otherwise another way will just to use a loop:
List<YourType> collection1 = new List<YourType>();
List<YourType> collection2 = new List<YourType>();
foreach(var item in baseCollection)
{
if(filterCollection.Contains(item.Property))
{
collection1.Add(item);
}
else
{
collection2.Add(item);
}
}

Iterate over a collection of strings using LINQ

I put the following code segment in .NET Fiddle but it printed out System.Linq.Enumerable+WhereArrayIterator1[System.String] I'd like to print out each content in result, in order to understand how Select works. Can someone please help to point out what the problem is? Many thanks!
string[] sequ1 = { "abcde", "fghi", "jkl", "mnop", "qrs" };
string[] sequ2 = { "abc", "defgh", "ijklm", "nop" };
var result =sequ1.Select( n1 => sequ2.Where(n2 => n1.Length < n2.Length) );
foreach( var y in result)
{
Console.WriteLine(y);
}
You are actually returning a collection of collections.
sequ1.Select( n1 => sequ2.Where(n2 => n1.Length < n2.Length) );
For each element in sequ1, this statement filters sequ2 to find all of the elements from the second sequence where the current value in the first sequence is shorter than it and then maps to a new collection containing each of those results.
To describe what Select is actually doing:
You start with a collection of things. In your case: sequ1 which has type IEnumerable<string>
You supply it with a function, this function takes an argument of the type of thing you supplied it with a collection of and has a return type of some other thing, in your case:
fun n1 => sequ2.Where(n2 => n1.Length < n2.Length)
Your function takes a string and returns an IEnumerable<string>
Finally, it returns a result containing a collection of each element in the original collection transformed to some new element by the function you supplied it with.
So you started with IEnumerable<string> and ended up with IEnumerable<IEnumerable<string>>.
That means you have a collection for each value that appears in sequ1.
As such, you would expect the result to be:
{{}, {"defgh", "ijklm"}, {"defgh", "ijklm"}, {"defgh", "ijklm"}, {"defgh", "ijklm"}}
You can inspect the results by adding another loop.
foreach(var y in result)
{
foreach(var z in result)
{
Console.WriteLine(z);
}
}
Change your Select to SelectMany:
var result = sequ1.SelectMany(n1 => sequ2.Where(n2 => n1.Length < n2.Length));
I may be wrong, but I think the OP wants to compare both arrays, and for each element, print the longest one.
If that's the case, I would do it as follows:
var result = sequ1.Take(sequ2.Length)
.Select((n1, i) =>
(n1.Length > sequ2.ElementAt(i).Length)
? n1
: sequ2.ElementAt(i));
Explanation:
Use Take to only go as long as the length of the second array, and avoid nullreference exceptions later on.
Use Select, with two arguments, the first is the string, the second is the index.
Use ElementAt to find the corresponding element in sequ2
I don't know about this example is about to help you to understand how select work. A more simple exmaple what i think is this.
public class Person {
public string Name { get; set; }
public string LastName { get; set; }
}
public class Test {
public Test() {
List<Person> persons = new List<Person>();
persons.Add(new Person() { Name = "Person1",LastName = "LastName1" });
persons.Add(new Person() { Name = "Person2",LastName = "LastName2" });
var getNamesFromPersons = persons.Select(p => p.Name);
}
}
If you are beginning c#, you need to sideline the keyword "var" from your code.
Force yourself to write out what the variables really are:
If you forego the use of var, you would have seen why your code was Console.Writing what it did.
string[] sequ1 = { "abcde", "fghi", "jkl", "mnop", "qrs", };
string[] sequ2 = { "abc", "defgh", "ijklm", "nop", };
IEnumerable<IEnumerable<string>> result = sequ1.Select(n1 => sequ2.Where(n2 => n1.Length < n2.Length));
foreach (IEnumerable<string> y in result)
{
foreach (string z in y)
{
Console.WriteLine(z);
}
}

customize OrderBy for a List?

I have a list of items and I want to create two ways to sort them, Alphabetically and Last Modified.
Here's what I did:
// Alphabetically
tableItems = tableItems.OrderBy (MyTableItem => MyTableItem.ItemName).ToList();
reloadTable(tableItems);
// Last Modified
tableItems = tableItems.OrderBy (MyTableItem => MyTableItem.Timestamp).ToList();
reloadTable(tableItems);
and this works perfectly fine.
My problem is I want this happen to all items in the list except for one.
This one item will always be constant and I want to make sure it's ALWAYS on the top of the list.
What would I need to do for that?
if it matters, c# is the lang.
Thank you for your time.
tableItems = tableItems.OrderBy(i => i.ItemName != "yourexceptitem").ThenBy(i => i.Timestamp).ToList();
EDIT:
If you want to sort the itemname except one, do like this,
tableItems = tableItems.OrderBy(i => i.ItemName != "TestSubject3").ToList();
Other, generic solution:
public static IEnumerable<T> OrderByExcept<T>(
this IEnumerable<T> source,
Predicate<T> exceptPredicate,
Func<IEnumerable<T>, IOrderedEnumerable<T>> projection)
{
var rest = new List<T>();
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
if (exceptPredicate(enumerator.Current))
{
yield return enumerator.Current;
}
else
{
rest.Add(enumerator.Current);
}
}
}
foreach (var elem in projection(rest))
{
yield return elem;
}
}
Usage:
tableItems = tableItems.OrderByExcept(
item => item.ItemName == "TestSubject3",
items => items.OrderBy(MyTableItem => MyTableItem.ItemName)
.ThenBy(MyTableItem => MyTableItem.TimeStamp))
.ToList();
Items that meets predicate will always be on the top of list, to the rest of elements projection will be applied.

C# Linq help improve performance?

Excuse my pseudo code below. I'm pretty sure there is a magical way to write this in a single linq statement that will also dramatically improve the performance. Here I have a list of millions of records in AList. The id may not be unique. What I'm after is the original list removing all duplicates (based on the id), but always grabbing the record with the earliest date. mystring is almost always a different value when there is a duplicate id.
public class A
{
public string id { get; set; }
public string mystring { get; set; }
public DateTime mydate { get; set; }
}
List<A> aListNew = new List<A>();
foreach (var v in AList)
{
var first = AList.Where(d => d.id == v.id).OrderBy(d => d.mydate).First();
// If not already added, then we add
if (!aListNew.Where(t => t.id == first.id).Any())
aListNew.Add(first);
}
You could use grouping directly to accomplish this in one LINQ statement:
List<A> aListNew = AList
.GroupBy(d => d.id)
.Select(g => g.OrderBy(i => i.mydate).First())
.ToList();
The fastest is probably going to be a straight foreach loop with a dictionary:
Dictionary<int, A> lookup = Dictionary<int, A>();
foreach (var v in AList)
{
if(!lookup.ContainsKey(v.id))
// add it
lookup[id] = v;
else if (lookup[id].mydate > v.mydate)
// replace it
lookup[id] = v;
}
// convert to list
List<A> aListNew = lookup.Values.ToList();
A Linq GroupBy / First() query might be comparable if there are few collisions, but either one is going to be O(N) since it has to traverse the whole list.
This should be easiest. No LINQ involved anyway.
var lookup = Dictionary<int, A>();
foreach(var a in aListNew.OrderByDescending(d => d.mydate)) {
lookup[a.id] = a;
}
var result = lookup.Values.ToList();
Note that sub-LINQ will hurt performance, and that's why I choose not to use it. Remember that LINQ is there to make your task easier, not to make the execution faster.

Filtering lists using LINQ

I've got a list of People that are returned from an external app and I'm creating an exclusion list in my local app to give me the option of manually removing people from the list.
I have a composite key which I have created that is common to both and I want to find an efficient way of removing people from my List using my List
e.g
class Person
{
prop string compositeKey { get; set; }
}
class Exclusions
{
prop string compositeKey { get; set; }
}
List<Person> people = GetFromDB;
List<Exclusions> exclusions = GetFromOtherDB;
List<Person> filteredResults = People - exclustions using the composite key as a comparer
I thought LINQ was the ideal way of doing this but after trying joins, extension methods, using yields, etc. I'm still having trouble.
If this were SQL I would use a not in (?,?,?) query.
Have a look at the Except method, which you use like this:
var resultingList =
listOfOriginalItems.Except(listOfItemsToLeaveOut, equalityComparer)
You'll want to use the overload I've linked to, which lets you specify a custom IEqualityComparer. That way you can specify how items match based on your composite key. (If you've already overridden Equals, though, you shouldn't need the IEqualityComparer.)
Edit: Since it appears you're using two different types of classes, here's another way that might be simpler. Assuming a List<Person> called persons and a List<Exclusion> called exclusions:
var exclusionKeys =
exclusions.Select(x => x.compositeKey);
var resultingPersons =
persons.Where(x => !exclusionKeys.Contains(x.compositeKey));
In other words: Select from exclusions just the keys, then pick from persons all the Person objects that don't have any of those keys.
I would just use the FindAll method on the List class. i.e.:
List<Person> filteredResults =
people.FindAll(p => return !exclusions.Contains(p));
Not sure if the syntax will exactly match your objects, but I think you can see where I'm going with this.
Many thanks for this guys.
I mangaged to get this down to one line:
var results = from p in People
where !(from e in exclusions
select e.CompositeKey).Contains(p.CompositeKey)
select p;
Thanks again everyone.
var thisList = new List<string>{ "a", "b", "c" };
var otherList = new List<string> {"a", "b"};
var theOnesThatDontMatch = thisList
.Where(item=> otherList.All(otherItem=> item != otherItem))
.ToList();
var theOnesThatDoMatch = thisList
.Where(item=> otherList.Any(otherItem=> item == otherItem))
.ToList();
Console.WriteLine("don't match: {0}", string.Join(",", theOnesThatDontMatch));
Console.WriteLine("do match: {0}", string.Join(",", theOnesThatDoMatch));
//Output:
//don't match: c
//do match: a,b
Adapt the list types and lambdas accordingly, and you can filter out anything.
https://dotnetfiddle.net/6bMCvN
You can use the "Except" extension method (see http://msdn.microsoft.com/en-us/library/bb337804.aspx)
In your code
var difference = people.Except(exclusions);
I couldn't figure out how to do this in pure MS LINQ, so I wrote my own extension method to do it:
public static bool In<T>(this T objToCheck, params T[] values)
{
if (values == null || values.Length == 0)
{
return false; //early out
}
else
{
foreach (T t in values)
{
if (t.Equals(objToCheck))
return true; //RETURN found!
}
return false; //nothing found
}
}
I would do something like this but i bet there is a simpler way. i think the sql from linqtosql would use a select from person Where NOT EXIST(select from your exclusion list)
static class Program
{
public class Person
{
public string Key { get; set; }
public Person(string key)
{
Key = key;
}
}
public class NotPerson
{
public string Key { get; set; }
public NotPerson(string key)
{
Key = key;
}
}
static void Main()
{
List<Person> persons = new List<Person>()
{
new Person ("1"),
new Person ("2"),
new Person ("3"),
new Person ("4")
};
List<NotPerson> notpersons = new List<NotPerson>()
{
new NotPerson ("3"),
new NotPerson ("4")
};
var filteredResults = from n in persons
where !notpersons.Any(y => n.Key == y.Key)
select n;
foreach (var item in filteredResults)
{
Console.WriteLine(item.Key);
}
}
}
This LINQ below will generate the SQL for a left outer join and then take all of the results that don't find a match in your exclusion list.
List<Person> filteredResults =from p in people
join e in exclusions on p.compositeKey equals e.compositeKey into temp
from t in temp.DefaultIfEmpty()
where t.compositeKey == null
select p
let me know if it works!
var result = Data.Where(x =>
{
bool condition = true;
double accord = (double)x[Table.Columns.IndexOf(FiltercomboBox.Text)];
return condition && accord >= double.Parse(FilterLowertextBox.Text) && accord <= double.Parse(FilterUppertextBox.Text);
});

Categories

Resources