22

Given a list:

    private List<KeyValuePair<string, string>> KV_List = new List<KeyValuePair<string, string>>();
    void initList()
    {
        KV_List.Add(new KeyValuePair<string, string>("qwer", "asdf"));
        KV_List.Add(new KeyValuePair<string, string>("qwer", "ghjk"));
        KV_List.Add(new KeyValuePair<string, string>("zxcv", "asdf"));
        KV_List.Add(new KeyValuePair<string, string>("hjkl", "uiop"));
    }

(NOTE: there are multiple values for the key "qwer" and multiple keys for the value "asdf".)

1) Is there a better way to return a list of all keys than just doing a foreach on the KeyValuePair List?

2) Similarly, is there a better way to return a list of all values for a given key than using a foreach?

3) And then, how about returning a list of keys for a given value?

Thanks...

frog_jr
  • 361
  • 1
  • 2
  • 8
  • Can You use a multimap like [this](http://stackoverflow.com/questions/380595/multimap-in-net)? – ntohl Jul 14 '15 at 18:33

7 Answers7

35
// #1: get all keys (remove Distinct() if you don't want it)
List<string> allKeys = (from kvp in KV_List select kvp.Key).Distinct().ToList();
// allKeys = { "qwer", "zxcv", "hjkl" }

// #2: get values for a key
string key = "qwer";
List<string> values = (from kvp in KV_List where kvp.Key == key select kvp.Value).ToList();
// values = { "asdf", "ghjk" }

// #3: get keys for a value
string value = "asdf";
List<string> keys = (from kvp in KV_List where kvp.Value == value select kvp.Key).ToList();
// keys = { "qwer", "zxcv" }
Jashaszun
  • 9,207
  • 3
  • 29
  • 57
5

It sounds like you would benefit from using something like:

Dictionary<string, List<string>> kvlist;

kvlist["qwer"] = new List<string>();
kvlist["qwer"].Add("value1");
kvlist["qwer"].Add("value2");

foreach(var value in kvlist["qwer"]) {
    // do something
}

It would be relatively easy to create a basic mutli-value dictionary class using a Dictionary and List.

This blog post talks more about Microsoft's MultiDictionary type available via NuGet.

Robert Horvick
  • 3,966
  • 21
  • 18
  • Unfortunately, I do not have control over the list (I have to "play it as it lies")... – frog_jr Jul 14 '15 at 19:39
  • Ah - that sucks. Several of the Linq-based answers should do what you want (though they will be more syntax-friendly, they won't be more efficient than simply looping). If you can ensure that the list is always sorted by the key value then you could improve performance from O(n) to O(log n). – Robert Horvick Jul 14 '15 at 20:23
  • It is typically unsorted (either by key or value), however, there are only a few thousands of entries in the list. I will probably select one of the linq answers, and they will "look" cleaner to anyone who looks at the code in the future. :) – frog_jr Jul 15 '15 at 16:03
5

You can use NameValueCollection from System.Collection.Specialized namespace:

NameValueCollection  KV_List = new NameValueCollection();

KV_List.Add("qwer", "asdf");
KV_List.Add("qwer", "ghjk");
KV_List.Add("zxcv", "asdf");
KV_List.Add("hjkl", "uiop");

Example of use:

string singleValue = KV_List["zxcv"];  // returns "asdf"
string[] values = KV_List.GetValues("qwer");  // returns "asdf, "ghjk"
string[] allKeys = KV_List.AllKeys;
string[] allValues = KV_List.AllKeys;

https://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection%28v=vs.110%29.aspx

Fabjan
  • 13,506
  • 4
  • 25
  • 52
4

Well you could definitly use your LINQ. But it's not "better" (in term of performance) since looping is already fast. It is perhaps more readable (personnal preference). For all the answers below, be aware that you need to have the System.Linq namespace imported. They also return IEnumerable<T> that are lazy loaded (executed when iterated over). If you want to return a concrete list, you can call the .ToList() extension.

Is there a better way to return a list of all keys than just doing a foreach on the KeyValuePair List?

KV_List.Select(kvp => kvp.Key);

Similarly, is there a better way to return a list of all values for a given key than using a foreach?

var theKeyToLookFor = "qwer";
KV_List.Where(kvp => kvp.Key == theKeyToLookFor).Select(kvp => kvp.Value);

And then, how about returning a list of keys for a given value?

var theValueToLookFor = "asdf";
KV_List.Where(kvp => kvp.Value == theValueToLookFor)
       .Select(kvp => kvp.Value)
       .ToList();

For more information on LINQ, look at LINQ (Language-Integrated Query)

Simon Belanger
  • 14,752
  • 3
  • 41
  • 35
1

1:

KV_List.Select(i => i.Key).ToList()

2:

KV_List.Where(i => i.Key == filterByKey).Select(i => i.Value).ToList()

3:

 KV_List.Where(i => i.Value == filterByValue).Select(i => i.Key).ToList()
serhiyb
  • 4,753
  • 2
  • 15
  • 24
1

I would use ILookup<K,V> in your case. It is like a dictionary but you can get the values as IEnumerable<V> with the same key.

ILookup<string, string> lookup = KV_List.ToLookup(x => x.Key, x => x.Value);
IEnumerable<string> list = lookup["qwer"];
foreach(string str in list)
{
    Console.WriteLine(str);
}

or simply

Console.WriteLine(string.Join(",", lookup["qwer"]));
EZI
  • 15,209
  • 2
  • 27
  • 33
1

Are you reading this and wondering why someone made some code have an IEnumerable<KeyValuePair<A,B>> instead of a Dictionary<A,B> but don't feel like asking and just want to get done?

if (collection == null)
    return null;

return collection 
    .Where(z => z.Key == aThing)
    .Select(z => z.Value)
    .FirstOrDefault();
Stachu
  • 5,677
  • 3
  • 30
  • 34