简体   繁体   中英

How to store the result of a linq query in a KeyDictionary variable

So I have a collection of objects who have multiple properties, two of these are groupname and personname. Now I need to count in the collection how many of each object belong to a certain group and person. So in other words, I need to group by groupname, then personname and then count how many objects have this combination. First I created this

public MultiKeyDictionary<string, string, int> GetPersonsPerGroup(IEnumerable<Home> homes ,List<string> gr, List<string> na)
        {
            List<string> groups = gr;
            groups.Add("");
            List<string> names = na;
            names.Add("");
            List<Home> Filtered = homes.ToList();
            Filtered.ForEach(h => h.RemoveNull());
            var result = new MultiKeyDictionary<string, string, int>();
        int counter1 = 0;
foreach (var g in groups)
    {
        int counter2 = 0;
        foreach (var n in names)
        { 
            int counter3 = 0;
            foreach (Home h in Filtered)
            {
                if (h.GroupName == g && h.PersonName == n)
                {
                    counter3++;
                    if (counter3 > 100)
                        break;
                }
            }
            if (counter3 > 0)
            {
                result.Add(g,n,counter3);
            }
            counter2++;
        }
        counter1++;

    }    

Which may look good, but the problem is that the "home" parameter can contain more than 10000 objects, with more than 1500 unique names and around 200 unique groups. Which causes this to iterate like a billion times really slowing my program down. So I need an other way of handling this. Which made me decide to try using linq. Which led to this creation:

var newList = Filtered.GroupBy(x => new { x.GroupName, x.PersonName })
                .Select(y => (MultiKeyDictionary<string, string, int>)result.Add(y.Key.GroupName, y.Key.PersonName, y.ToList().Count));    

Which gives an error "Cannot convert type ' void ' to 'MultiKeyDictionary<string,string,int>' and I have no idea how to solve it. How can I make it so that the result of this query gets stored all in one MultikeyDictionary without having to iterate over each possible combination and counting all of them.

Some information:

  • MultiKeyDictionary is a class I defined (something I found on here actually), it's just a normal dictionary but with two keys assosiated to one value.

  • The RemoveNull() method on the Home object makes sure that all the properties of the Home object are not null. If it is the case the value gets sets to something not null ( "null", basic date, 0, ... ).

  • The parameters are: homes = a list of Home objects received from an other class gr = a list of all the unique groups in the list of homes na = a list of all the unique names in the list of homes

  • The same name can occur on different groups

Hopefully someone can help me get further! Thanks in advance!

Select must return something. You are not returning but only adding to an existing list. Do this instead:

var newList = Filtered.GroupBy(x => new { x.GroupName, x.PersonName }):

var result = new MultiKeyDictionary<string, string, int>);
foreach(var y in newList)
{
    result.Add(y.Key.GroupName, y.Key.PersonName, y.ToList().Count));    
}

The reason you are getting error below:

"Cannot convert type 'void' to 'MultiKeyDictionary'

is because you are trying to cast the returned value from Add which is void to MultiKeyDictionary<string,string,int> which clearly cannot be done.

If MultiKeyDictionary requires the two keys to match in order to find a result, then you might want to just use a regular Dictionary with a Tuple as a composite type. C# 7 has features that make this pretty easy:

public Dictionary<(string, string), int> GetPersonsPerGroup(IEnumerable<Home> homes ,List<string> gr, List<string> na)
{
    return Filtered.GroupBy(x => (x.GroupName, x.PersonName))
        .ToDictionary(g => g.Key, g => g.Count);
}

You can even associate optional compile-time names with your tuple's values, by declaring it like this: Dictionary<(string groupName, string personName), int> .

Your grouping key anonymous object should work fine as a standard Dictionary key, so no reason to create a new type of Dictionary unless it offers special access via single keys, so just convert the grouping to a standard Dictionary :

var result = Filtered.GroupBy(f => new { f.GroupName, f.PersonName })
                     .ToDictionary(fg => fg.Key, fg => fg.Count());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM