LINQ - Select correct values from nested collection - c#

Consider the following class hierarchy:
public class Foo
{
public string Name { get; set; }
public int Value { get; set; }
}
public class Bar
{
public string Name { get; set; }
public IEnumerable<Foo> TheFoo { get; set; }
}
public class Host
{
public void Go()
{
IEnumerable<Bar> allBar = //Build up some large list
//Get Dictionary<Bar, Foo> with max foo value
}
}
What I would like to do using Linq2Objects is to get an KeyValuePair where for each Bar in the allBBar collection we select the Foo with the maximum Value property. Can this be done easily in a single LINQ statement?

Sure, although my preferred solution uses MaxBy from MoreLINQ:
var query = allBar.ToDictionary(x => x, // Key
x => x.TheFoo.MaxBy(f => f.Value));
Note that this will go pear-shaped if TheFoo is empty for any Bar instance.

Another way using Aggregate instead of OrderBy so that figuring out the max Foo is O(n) instead of O(n log n):
var query = allBar.ToDictionary(
bar => bar,
bar => bar.TheFoo.Aggregate(
null,
(max, foo) => (max == null || foo.Value > max.Value) ? foo : max));

just to add to Jon's comment about MaxBy going pear shaped if you have no foos, you could do an OrderByDescending and then use FirstOrDefault to get at the Max element. If the collection is empty it'd just return null instead of going "pear shaped".
var foobars = bars.ToDictionary(bar => bar,
bar => bar.TheFoo.OrderByDescending(foo => foo.Value).FirstOrDefault());
I don't think this wouldn't be as efficient as MaxBy, but it'd be more robust in the case of an empty collection.

Related

How to get distinct values from nested lists of objects?

I have the following problem:
foo f = new foo();
// ...
f.bars.SelectMany(m => m.qux.GroupBy(x => x.hash))
.Select(group => group.First())
.Sum(s => s.size);
This returns the sum of the total size property for qux within a single bar instance. Adding another qux object with an existing hash value in another bar is ignored so its size value is counted in the computed sum. (Even tho the same qux exists in another bar object).
How can I tweak my query to make sure all objects having the same hash are calculated once?
The data structure would look like this:
class foo
{
public List<bar> bars = new List<bar>();
}
class bar
{
public List<qux> qux = new List<qux>();
}
class qux
{
public string hash { get; set; }
public int size { get; set; }
}
Judging from your data structure, here is what you need:
int sizeSum =
bars.SelectMany(m => m.qux)
.GroupBy(q => q.hash)
.Select(g => g.First())
.Sum(q => q.size);
The difference with your initial solution is that we first get a unified list with .SelectMany(), and only then do we group it by hash.

Binary Search with List in c#

I am using c# WPF for developing a Windows Application.
The application requires a class as follows
public class Limits
{
public String col1
{
get;
set;
}
public String col2
{
get;
set;
}
public String col3
{
get;
set;
}
}
I am using a List to Store Objects like:-
List myList<Limits> = new List<Limits>();
"myList" has around 15000 Objects.
Now, I want to search this myList for a particular attribute.
Eg: I want to find out the object that has col1 set as "abc".
How can I use Binary Search for this problem?
First of all, the list has to be sorted on the col1 property for you to be able to use binary search at all.
You would need a comparer that compares the col1 property:
public class LimitsComparer : IComparer<Limits> {
public int Compare(Limits x, Limits y) {
return x.col1.CompareTo(y.col1);
}
}
Then you can use that to do the binary search:
int index = myList.BinarySearch(new Limits { col1 = "abc" }, new LimitsComparer());
The index returned is:
The zero-based index of item in the sorted List, if item is found;
otherwise, a negative number that is the bitwise complement of the
index of the next element that is larger than item or, if there is no
larger element, the bitwise complement of Count.
You can also use the Where method to get the objects that has that property:
List<Limits> result = myList.Where(x => x.col1 == "abc").ToList();
Although that is not quite as efficient, you should still consider if that is a better solution as it's easier to implement and gives a result that is easier to handle. Also (and this might be more important), it works even if the list isn't sorted on col1.
You could use somthing like this.
myList.Where(i => i.col1 == "abc").ToList();
Use a dictionary where the keys are stored in a hash table. Linq will create the cdictionary easily.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication41
{
class Program
{
static void Main(string[] args)
{
List<Limits> myList = new List<Limits>();
//dictionary with unique keys
Dictionary<string, Limits> dict1 = myList.AsEnumerable()
.GroupBy(x => x.col2, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
//dictionary with keys having multiple values
Dictionary<string, List<Limits>> dict2 = myList.AsEnumerable()
.GroupBy(x => x.col2, y => y)
.ToDictionary(x => x.Key, y => y.ToList());
Limits abc = dict1["abc"];
}
}
public class Limits
{
public String col1 { get; set; }
public String col2 { get; set; }
public String col3 { get; set; }
}
}
unless you explicitly want to use binary search, you should use the standard Linq functions available to you. Unless your list is already sorted, this might be more efficient than binary sort.
var myList = new List<Limits> {....}
var entry = myList.Where(l => l.col1== "abc").FirstOrDefault();
if(entry == null)
{ // no match found }
If you really want binary search, ref Can LINQ use binary search when the collection is ordered?

LINQ - Getting all child records from all parents

I have two models:
class Foo
{
public List<Bar> Bars { get; set; }
}
class Bar
{
public int Value { get; set; }
}
Having an instance of List<Foo>, how can I get all Value using a LINQ query?
Thank you all
SelectMany is normally the way to flatten hierarchies, so:
var values = myList.SelectMany(foo => foo.Bar)
.Select(bar => bar.Value);
The SelectMany will give you an IEnumerable<Bar>, and then the Select projects that sequence of Bar objects to the Value property of each, returning an IEnumerable<int>.
As a query expression, this would be:
var values = from foo in myList
from bar in foo.Bar
select bar.Value;
I suggest you to change List<Bar> property name to Bars.
And firstly use SelectMany(). It projects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence. And then use Select() to project each element of a new sequence as you wish.
var result = myList.SelectMany(x => x.Bars).Select(x => x.Value).ToList();
Use SelectMany instead of Select
var result = LIST1.SelectMany(x => x.LIST2.Select(y => y.Value)).Tolist();

List<MyObject> Contains

What's the fastest way (coding wise) to check if one entry exist on a list?
MyObject has 2 properties
public class Name
{
public string FirstName{ get; set; }
public string LastName { get; set; }
}
then I have another class like this:
public class Foo
{
private List<Name> Names : new List<Name>();
public List<Name> Names { get; set; }
public bool Contains(Name x)
{
if (x == null)
return false;
>>> Navigate || Equals || Linq.Contains
>>> What's the easiest way to do this?
}
}
Fastest for List are O(n) lookup speed and O(1) insert speed:
Atleast One
Names.Any(n=> x.FirstName == n.FirstName && x.LastName == n.LastName)
Exactly One:
Names.Count(n=> x.FirstName == n.FirstName && x.LastName == n.LastName) == 1
Any() is faster because it short circuits when it finds the first instance of Name. Count searches through the list everytime to find all instances of Name.
Instead, you could use a Collection (e.g. HashSet, Dictionary, etc) where lookup operations are O(1). However, collections don't hold the same properties as Lists. Note, Hashset<string> where names are stored as something like FirstName + (delimeter) + LastName is faster than any other option you have.
You could also use a SortedList where lookup speeds are O(log(n)). However, inserting elements in a sorted list is O(nlog(n)) because you must keep the list sorted after every insertion.
I would say linq .Any is pretty easy
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.any.aspx
Names.Any(n=> n==x)
Using Linq should be easier to read.
Here is sample using Any.
public bool Contains(Name x)
{
if (x == null)
return false;
return this.Names.Any(item => item.FirstName == x.FirstName && item.LastName == x.LastName);
}
Suggestion: If the items in your list are supposed to be unique then you could use System.Collections.Generic.HashSet and use System.Linq.Enumerable.Contains..
You might want to compare for the performance with the methods Contains and Any of the following code:
partial class Foo {
class NameComparer: IComparer<Name> {
public int Compare(Name x, Name y) {
return
object.ReferenceEquals(x, y)
||y.LastName==x.LastName&&y.FirstName==x.FirstName?0:~0;
}
public static readonly NameComparer Default=new NameComparer();
}
public bool Any(Name x) {
return
Names.Any(
y => object.ReferenceEquals(x, y)
||y.LastName==x.LastName&&y.FirstName==x.FirstName);
}
public bool Contains(Name x) {
return Names.BinarySearch(x, NameComparer.Default)>~0;
}
}

Find Index from List

I have a Dictionary that contains thread Information Dictionary<String,Thread>
"2FF"
"2IE"
"7CH"
etc
what i know is integers 2,7 etc what i want to know that in Dictionary how many strings contain the given integer if it is there then get that string
Eg
String GetString(int integer)
{
//if Dictionary contains given intgr return whole string in which that integer is present
}
}
With LINQ syntax:
var matchingThreads = from pair in dictionary
where pair.Key.StartsWith(number.ToString())
select pair.Value;
With traditional syntax:
var matchingThreads = dictionary
.Where(pair => pair.Key.StartsWith(number.ToString()))
.Select(pair => pair.Value);
If you only need to count them and you don't care about the Thread objects, you can use:
int count = dictionary.Keys.Count(key => key.StartsWith(number.ToString()))
Note that you need a using System.Linq directive.
Maybe a List<CustomClass> would be a better choice here where CustomClass would look like:
public sealed class CustomClass
{
public Thread Thread { get; set; }
public string String { get; set; }
}
(Better property names are alway good, of course :-) )
A dictionary is not sutitable if you do not know the exact keys or only parts of them.
You could then use LINQ to find out what you want, e.g.:
int count = list.Where(c => c.String.StartsWith(integer.ToString())).Count();
//or
IEnumerable<string> strings = list.Where(c => c.String.StartsWith(integer.ToString())).Select(c => c.String);
public IEnumerable<string> GetMatchingKeys(int value)
{
var valueText = value.ToString();
return _dictionary.Keys.Where(key => key.Contains(valueText));
}

Categories

Resources