Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare keys of dictionary with values of another dictionary

I have two dictionaries, both of type Dictionary<string, List<List<string>>. I want to select the range of entries from dictionary 1 which match the values/entries from dictionary 2.

E.g. dictionary 1 has the keys 01, 01.1 and dictionary 2 has the matching values 01, 01.1. at key 1

First I get the entries from dictionary 2 at key 1 like this:

var dictList = (from entry in DbDict
                where entry.Key == id.ToString() // id = 1
                select entry).ToList();

I tried to select these via linq with something like this:

var matchingEntries = (from entry in ExcelDict
                       where ExcelDict.Keys.Equals(dictList[0].Value)
                       select entry).ToList();

and tried more along those lines, but it won't return any results.

How do I get the range of valuepairs from dictionary 1, whose keys match the values of dictionary 2?

Edit1:

Dictionary 1: 
Key     Value
01      "these", "are"
        ..., ...
01.1    "just", "some"
        ..., ...
02      "sample", "values"
        ..., ...

Dictionary 2:
Key     Value
1       "01", "01.1"
        "foo", "bar"
        ..., ...                
2       "02", "02.21"
        "value1" "value2"

Edit2:

Expected output:

"01", "01.1"
"foo", "bar"

Edit3:

Compilable input as requested in the comments. This is exactly the structure I'm dealing with:

var dict1 = new Dictionary<string, List<List<string>>>();

dict1.Add("01", new List<List<string>> { new List<string> { "these" }, new List<string> { "are" } });
dict1.Add("01.1", new List<List<string>> { new List<string> { "just" }, new List<string> { "some" } });
dict1.Add("02", new List<List<string>> { new List<string> { "sample" }, new List<string> { "values" } });


var dict2 = new Dictionary<string, List<List<string>>>();
dict2.Add("1", new List<List<string>> { new List<string> { "01", "01.1" }, new List<string> { "foo", "bar" } });
dict2.Add("2", new List<List<string>> { new List<string> { "02", "value1" }, new List<string> { "02.21", "value2" } });

Edit4:

Sorry for the late response. Both Gaurang Dave's suggestion in the comments and the accepted answer worked for me. Thanks for everyone's help!

like image 639
Allix Avatar asked Mar 06 '23 18:03

Allix


1 Answers

You wrote:

I want to select the range of entries from dictionary 1 which match the values/entries from dictionary 2.

From your output in Edit2, it seems that you want to take the values from Dictionary 2. You don't do anything with the Keys. Each value is a List<List<string>>. In your example all strings in the first list of the value with Key 1 have a corresponding key in dictionary 1. Apparently that is the condition to decide that the complete value is in your output.

The first list of the value with Key 2 has an element which is not a key in dictionary 1. Hence nothing of the value is in the output.

Unclear: what if the 2nd list would match instead of the 1st list?

Key     Value
3       "foo", "bar"
        "01", "01.1"

Should this also be in your final result?

Unclear Do you want as result a List<List<string>>, or do you want one big List<string> with all matching values? What about duplicate values?

Let's assume you only want to check the first List in your List of Lists:

We'll only look at the values from Dictionary 2, the keys are discarded. Then from every list in this value collection we take the first one (if there is one), and as a separate property remember the complete list.

Of course empty lists should not be in the end result, hence we keep only those that have a first element:

// Only use the values of dictionary 2:
IEnumerable<List<List<string>>> dict2Values = dictionary2.Values

// then for easy comparison extract the first list
var separatedFirstList = dict2Values.Select(listOfLists=> new
{
     FirstList = listOfLists.FirstOrDefault(), // this is a List<string> or null
     AllLists = listOfLists,    // original List<List<string>> where FirstList is the first
})

// keep only the elements that have a first list:
.Where(stringListWithFirstElement => stringListWithFirstElement.FirstList != null);

By now we have transformed your example dictionary into:

{
    FirstString = {"01", "01.1"},
    FullList =    {"01", "01.1"}, {"foo", "bar"}, {...}, {...},
},
{
    FirstString = {"02", "02.21"},
    FullList =    {"02", "02.21"}, {"value1" "value2"}, ...
},
{
     FirstString = ...,
     FullList = ...,
},
...

From this sequence we only want to keep those WHERE ALL elements in the FirstString are keys of Dictionary 1:

IEnumerable<string> keysDictionary1 = dictionary1.Keys;
var matchingItems = separatedFirstList
    .Where(item => item.FirstList.All(txt => keysDictionary1.Contains(txt));

You see the Where and the All.

Result:

{
    FirstString = {"01", "01.1"},
    FullList =    {"01", "01.1"}, {"foo", "bar"}, {...}, {...},
},
...

The one with FirstString = {"02", "02.21"} is removed, because not all elements of firstString where a Key in dictionary 1,

Finally: get rid of the FirstString:

List<List<String>> finalResult = matchingItems
    .Select(matchingItem => matchingItem.FullList);

Or if you want the result to be one List<String>:

List<string> finalResult = matchingItems.SelectMany(matchingItem => matchingItem.FullList);

TODO: consider creating one big LINQ statement. As the intermediate steps use lazy execution I'm not sure whether this will improve performance. However I'm sure that it will decrease readability.

like image 160
Harald Coppoolse Avatar answered May 10 '23 19:05

Harald Coppoolse