user2443476
user2443476

Reputation: 1975

Use linq expression to filter a dictionary with a list of keys

I have a dictionary containing all users with their corresponding age.

Dictionary<string,int> AllUsers;

I have a list of specific users.

List<String> Users;

I would like to filter the first dictionary AllUsers with only the users who have their name in the SpecificUsers list.

I have done something manually with loops but I would like to use linq expression but I am not very familiar with them.

Thanks in advance for your help

Upvotes: 8

Views: 10335

Answers (5)

Rohit
Rohit

Reputation: 10236

There are multiple ways to do this

You can use this using where keyword

 var result= yourDictionary.Where(p=> yourList.Contains(p.Key))
     .ToDictionary(p=> p.Key, p=> p.Value);

But if you have lot of entries its better to use HashSet

var strings = new HashSet<string>(yourList);
var result= yourDictionary.Where(p=> strings.Contains(p.Key))
        .ToDictionary(p=> p.Key, p=> p.Value);

using JOIN

 var query =
            from kvp in yourDictionary
            join s in yourList on kvp.Key equals s
            select new { kvp.Key, kvp.Value };

Upvotes: 2

Ivan Stoev
Ivan Stoev

Reputation: 205569

With the help of the following useful function

public static class Extensions
{
    public static KeyValuePair<TKey,TValue>? Find<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key)
    {
        TValue value;
        return source.TryGetValue(key, out value) ? new KeyValuePair<TKey, TValue>(key, value) : (KeyValuePair<TKey, TValue>?)null;
    }
}

here is IMO the optimal solution (uses single lookup per key and does not introduce closure):

var filteredUsers = Users.Select(AllUsers.Find)
    .Where(item => item.HasValue)
    .ToDictionary(item => item.Value.Key, item => item.Value.Value);

Upvotes: 0

Vincent
Vincent

Reputation: 3746

You can use a join() method to actually join the two collections. It allows us to get what you need with a single line of linq.

var allUsers    = new Dictionary<string, int>();
allUsers.Add("Bob", 10);
allUsers.Add("Tom", 20);
allUsers.Add("Ann", 30);

var users       = new List<string>();
users.Add("Bob");
users.Add("Tom");
users.Add("Jack");

var result  = allUsers.Join(users, o => o.Key, i => i, (o, i) => o);
foreach(var r in result)
{
    Console.WriteLine(r.Key + " " + r.Value);
}

It will output the following in the console:

Bob 10
Tom 20

Only the names that appears in both collection will be available in the result collection

Upvotes: 3

Kamil Budziewski
Kamil Budziewski

Reputation: 23087

It might work

var newdict = AllUsers.Where(x => Users.Contains(x.Key))
                                        .ToDictionary(val => val.Key, val => val.Value);

it will create new dictionary (cause linq is for querying not updating) with all the users from dictionary that are on the Users list. You need to use ToDictionary to actualy make it dictionary.

EDIT: As @Rawling said it would be more performant to filter on Dictionary rather than on list. Solution to achieve that is present in @Luaan answer (I won't copy it as some do)

Upvotes: 6

Luaan
Luaan

Reputation: 63722

You could filter Users:

Users.Where(i => AllUsers.ContainsKey(i)).Select(i => new { User = i, Age = AllUsers[i] });

The major benefit of this is that you're using the indexed AllUsers to do the filtering, so your total computational complexity only depends on the amount of users in Users (Dictionary.Contains is O(1)) - the naïve approaches tend to be Users * AllUsers.

If you want a dictionary on output, it's as simple as replacing the .Select(...) above with

.ToDictionary(i => i, i => AllUsers[i])

Upvotes: 6

Related Questions