LINQ group by multiple attributes and create dictionary

I have a list of objects from a class called ScrewBoltPattern.

I want to create a dictionary which contains the number of appearances of each family of ScrewBoltPattern. To decide if a screw belongs to a family I use some properties of that class.

To simplify this query let's say I use the properties Length and Diameter.

I want to create a Dictionary which has keys formatted like screw.Length + "_" + screw.Diameter

How can I get this?

This is what I've done so far

Dictionary<string, int> boltFamilyList = selectedBolts
                .GroupBy(bolt => new { bolt.Length, bolt.Diameter })
                .ToDictionary(bolt => bolt.Key, bolt => bolt.Count());

I need to give format to the dictionary key somewhere, but I don't know how to do it.

You can format the key in the group by:

Dictionary<string, int> boltFamilyList = selectedBolts
    .GroupBy(bolt => $"{bolt.Length}_{bolt.Diameter}")
    .ToDictionary(bolt => bolt.Key, bolt => bolt.Count());

Your group key (and by proxy your dictionary key) will be that formatted string.

Try it online

Dictionary<string, List<string>> groups. Somewhere to put the results. rawFiles.GroupBy(r = > GetDateFromPath(r)) Group the input paths by just the info we are interested in - the date info becomes the dictionary Key, and the Group contains the paths that have the same date info..ToDictionary(g = > g.Key, g = > g.ToList()); Ah!

You can also use an ILookup to achieve the same goal:

ILookup<string, int> lookup = 
    selectedBolts.ToLookup(bolt => $"{bolt.Length}_{bolt.Diameter}");

and then

int count = lookup["12_36"].Count();

In this example, I am going create bunch of products with some sample data. Later, I will group them by subcategoryname using GroupBy clause in LinQ and then convert the grouped list into a generic dictionary. As you can see below, I have created a simple product class for our example. Product.cs: I have created …

Although you already have the solution, just want to point how you could solve it because you were really close to the solution...

Dictionary<string, int> boltFamilyList = selectedBolts
    .GroupBy(bolt => new { bolt.Length, bolt.Diameter })
    .ToDictionary(bolt => bolt.Key, bolt => bolt.Count());

in the list line you could create key:

.ToDictionary(bolt => $"{bolt.Key.Length}_{bolt.Key.Diameter}", bolt => bolt.Count());

If you look at the signature of the Enumerable.ToDictionary method you will see that the first argument is Func<TSource,TKey> keySelector, in your case the TSource is anonymous type and the TKey is string. All you need to do is to define mapping between TSource and TKey and that is exactly what the function bolt => $"{bolt.Key.Length}_{bolt.Key.Diameter}" does.

GroupBy operator projects data in groups based on common attribute or key.It is a great tool when you want to see the summarize result based on group. In this article, I have written several programs that is easy to understand and will help you to understand GropuBy operator.

You may not be aware of this solution as well, you may not need string formatting at all. (you can use C# 7 with value tuples)

Dictionary<(int length, int diameter), int> boltFamilyList = selectedBolts
    .GroupBy(bolt => (bolt.Length, bolt.Diameter))
    .ToDictionary(bolt => bolt.Key, bolt => bolt.Count());

And access like

dic.TryGetValue((10, 20), out int count);

Where 10 and 20 are length and diameter

Once I get to the point where I’m using LINQ to group by multiple columns, my instinct is to back out of LINQ altogether. However, I recognize that this is just my personal opinion. If you’re struggling with grouping by multiple columns, just remember that you need to group by an anonymous object.

Group query results. 12/01/2016; 8 minutes to read +3; In this article. Grouping is one of the most powerful capabilities of LINQ. The following examples show how to group data in various ways: By a single property. By the first letter of a string property. By a computed numeric range. By Boolean predicate or other expression. By a compound key.

To remove a student who doesn't have a StandardID, use a where operator before the group operator: Example: LINQ GroupBy Query - C# var studentsGroupByStandard = from s in studentList where s.StandardID > 0 group s by s.StandardID into sg orderby sg.Key select new { sg.Key, sg };

source: It is the Collections.Generic.IEnumerable<T> collection from which we need to create a System.Collections.Generic.Dictionary<TKey,TValue> collection. keySelector: It is a function that is basically used to extract a key from each element. Type parameters: TSource: The type of the elements of the source sequence.

Comments
  • And where do I specify the properties I want to use to group them?
  • Are you grouping by different properties to your formatted key? If that's the case, can you edit your question to reflect this?
  • @No, I'm not. Will that do it?
  • The code I've provided will group by the string value generated by $"{bolt.Length}_{bolt.Diameter}", which will also be your key in the dictionary. It should work as you expect.
  • Which has the advantage that you don't get an exception like with a dictionary if the key isn't present and you could use other methods apart from Count
  • @Rango Indeed, + this code avoids the double enumeration (bolts and GroupBy entries) in the OP's code. The null key handling could also be handy but not in this case.
  • I don't think it's more efficient than OP's code. ToLookup needs to enumerate the list and create groups by a key like GroupBy+ToDictionary does, just in one method instead of two but with the same complexity. But it's more flexible because it allows to evaluate more than the count if you want, without creating a new dictionary.
  • @Rango Except than ToDictionary has to check whether each entries already exists. I agree, not a big deal though.