Having IEnumerable<Order> orders, how to get a Dictionary<string, IEnumerable<Order>> using Linq, where the key is Order.CustomerName mapped to a IEnumerable of customer's orders.
orders.ToDictionary(order => order.CustomerName) is not going to work right away, since there could be multiple orders that could have the same CustomerName.
Solution: orders.ToLookup(order => order.CustomerName);
The ILookup interface is designed for this purpose, and represents a dictionary-like structure that contains many values per key. It has similar performance characteristics to those of a dictionary (i.e. it's a hashtable type structure)
You can create an ILookup using the .ToLookup extension method as follows:
ILookup<string, Order> ordersLookup = orders.ToLookup(o => o.CustomerName)
then:
IEnumerable<Order> someCustomersOrders = ordersLookup[someCustomerName];
Just an alternative to #spender's answer, if you really want a type Dictionary<string, IEnumerable<Order>>, you could use:
Dictionary<string, IEnumerable<Order>> dictionary = orders
.GroupBy(order => order.CustomerName)
.ToDictionary(groupedOrders => groupedOrders.Key,
groupedOrders => (IEnumerable<Order>)groupedOrders);
I'm sure there's a nicer way, but that'll do it too.
Or you could probably simply use
orders.ToLookup(o => o.CustomerName).ToDictionary(g => g.Key)
But as Spender's answer indicates, maybe you don't need the last method, ToDictionary.
Related
I have a dictionary of customers and associated data (e.g. MoneySpent) and would like to return the sorted list of customers only.
The only solution I figured out so far is this:
CustomerData<Customer, int> //the value here is the money spent
List<KeyValuePair<Customer, int>> sortedListByValue = CustomerData.OrderByDescending(s => s.Value).ToList();
Then just going throug the list and obtaining keys. However, I am sure there is an easier way of doing and would be glad for advice.
You can select the Key which will give you the customers.
var sortedListByValue = CustomerData.OrderByDescending(s => s.Value)
.Select(x => x.Key).ToList();
I have a huge list of Dictionary like
List<Dictionary<String,SomeType>> Dict = new List<Dictionary<string,SomeType>>();
I need to convert to the list of dictionary of some other type like below:
List<Dictionary<String,SomeOtherType>> AnotherDict;
Is there a better approach than foreach on the 'Dict' List.
Yes, you can use LINQ:
AnotherDict = Dict.Select(d => d.ToDictionary(pair => pair.Key,
pair => Convert(pair.Value)))
.ToList();
Where Convert is whatever conversion you want to apply to SomeType to construct a SomeOtherType.
EDIT: As noted in comments, this is no more efficient than using nested foreach loops yourselves. It's just simpler.
In C#, I have an object type 'A' that contains a list of key value pairs.
The key value pairs is a category string and a value string.
To instantiate object type A, I would have to do the following:
List<KeyValuePair> keyValuePairs = new List<KeyValuePair>();
keyValuePairs.Add(new KeyValuePair<"Country", "U.S.A">());
keyValuePairs.Add(new KeyValuePair<"Name", "Mo">());
keyValuePairs.Add(new KeyValuePair<"Age", "33">());
A a = new A(keyValuePairs);
Eventually, I will have a List of A object types and I want to manipulate the list so that i only get unique values and I base it only on the country name. Therefore, I want the list to be reduced to only have ONE "Country", "U.S.A", even if it appears more than once.
I was looking into the linq Distinct, but it does not do what I want because it I can't define any parameters and because it doesn't seem to be able to catch two equivalent objects of type A. I know that I can override the "Equals" method, but it still doesn't solve the my problem, which is to render the list distinct based on ONE of the key value pairs.
To expand upon Karl Anderson's suggestion of using morelinq, if you're unable to (or don't want to) link to another dll for your project, I implemented this myself awhile ago:
public static IEnumerable<T> DistinctBy<T, U>(this IEnumerable<T> source, Func<T, U>selector)
{
var contained = new Dictionary<U, bool>();
foreach (var elem in source)
{
U selected = selector(elem);
bool has;
if (!contained.TryGetValue(selected, out has))
{
contained[selected] = true;
yield return elem;
}
}
}
Used as follows:
collection.DistinctBy(elem => elem.Property);
In versions of .NET that support it, you can use a HashSet<T> instead of a Dictionary<T, Bool>, since we don't really care what the value is so much as that it has already been hashed.
Check out the DistinctBy syntax in the morelinq project.
A a = new A(keyValuePairs);
a = a.DistinctBy(k => new { k.Key, k.Value }).ToList();
You need to select the distinct property first:
Because it's a list inside a list, you can use the SelectMany. The SelectMany will concat the results of subselections.
List<A> listOfA = new List<A>();
listOfA.SelectMany(a => a.KeyValuePairs
.Where(keyValue => keyValue.Key == "Country")
.Select(keyValue => keyValue.Value))
.Distinct();
This should be it. It will select all values where the key is "Country" and concat the lists. Final it will distinct the country's. Given that the property KeyValuePairs of the class A is at least a IEnumerable< KeyValuePair< string, string>>
var result = keyValuePairs.GroupBy(x => x.Key)
.SelectMany(g => g.Key == "Country" ? g.Distinct() : g);
You can use the groupby statement. From here you can do all kind off cool stuf
listOfA.GroupBy(i=>i.Value)
You can groupby the value and then sum all the keys or something other usefull
The two lines below return an IGrouping<string, DataRow>:
var mbVals = GetMBValues_MBvsPU(mbRptDataPkg);
var puVals = GetPUValues_MBvsPU(puRptDataPkg);
I'd though that you could access the grouping's data like this mbVals[StringKey] but that doesn't look possible. I can do a foreach over the DataRows but it just seems to me one should be able to easily access through a linq expression somehow.
What I'd like to do is compare fields in the datarows from one with fields from datarows of the other through the keys.
Any thoughts?
Thanks!
IGrouping<> is IEnumerable<> so you use the ElementAt() extension method.
But for the situation you describe, you may be better off using Zip() to unify the two groups (if the items are in the same order and match exactly) or using Join() if they don't.
An instance that implements IGrouping<T, U> has a (one) key of type T. Since you want to compare based on keys (plural), an IGrouping<string, DataRow> isn't what you need.
You need an IEnumerable<IGrouping<string, DataRow>> or an ILookup<string, DataRow>. Something that has many keys.
ILookup<string, DataRow> source1 = GetSource1();
ILookup<string, DataRow> source2 = GetSource2();
var BothKeyed =
(
from key in source1.Select(g => g.Key).Union(source2.Select(g => g.Key))
select new
{
Key = key,
In1 = source1[key],//note, In1 may be empty.
In2 = source2[key] //note, In2 may be empty.
}
).ToLookup(x => x.Key);
I have a synchronized Hashtable with int as the key, and a custom class called Job as the value. I would like to filter this Hashtable based on a property in my Job class called JobSize. JobSize is just an enum with values Small, Medium, and Large.
It's fine if it needs to be converted to another collection type to do this.
I know there's a slick LINQy way to do this, but I haven't found it yet...
It looks like this will work for me:
var smallJobs = hashTable.Values.Cast<Job>().Where(job => job.JobSize == JobSize.Small);
The ".Cast<Job>()" is required because Hashtable is non-generic.
Do you need to maintain the keys and values in your filtered Hashtable? If so, try this.
It'll filter the Hashtable and return the filtered results as a strongly typed Dictionary<int,Job>:
var filtered = yourHashtable.Cast<DictionaryEntry>()
.Where(x => ((Job)x.Value).JobSize == JobSize.Small)
.ToDictionary(x => (int)x.Key, x => (Job)x.Value);
You should be able to use something like this:
IEnumerable<Job> smallJobs
= hashTable.Values.Cast<Job>.Where(job => job.JobSize == JobSize.Small);