0

I've a requirement to implement two keys in a dictionary and stuck a bit. I am not sure if it's possible to do but my criteria is to make a search option using two keys to match in a dictionary data structure similar to the below Linq:

if(id > 0 && status != null)
{
    var result = (from c in db.Employees
                  where c.EmployeeId == id && c.Status == status
                  select c).ToList();
}

With Dictionary, I've tried the following:

public class Employee
{
   public int EmployeeId { get; set; }
   public string EmployeeName { get; set; }
   public string Address { get; set; }
   public string Status { get; set; }
}

public class TwoKeyDictionary<k1, k2, T> : Dictionary<k2, Dictionary<k2, T>>
{

}

Finally tried to the bind the Employee class with data and used List<> for that:

List<Employee> employees = new List<Employee>()
{
    new Employee { EmployeeId = 1001, EmployeeName = "John", Address = "On Earth", Status = "Active"},
    new Employee { EmployeeId = 1002, EmployeeName = "Jack", Address = "On Earth", Status = "Active"},
    new Employee { EmployeeId = 1003, EmployeeName = "James", Address = "On Earth", Status = "Inactive"},
    new Employee { EmployeeId = 1004, EmployeeName = "Oswald", Address = "On Earth", Status = "Inactive"}
};

int id = 0;
string status = "" 

if (id > 0 && status != "")
{
    id = Convert.ToInt32(Console.ReadLine())
    status = Console.ReadLine().ToUpper();
}

TwoKeyDictionary<int, string, List<Employee>> dict = 
          employees.GroupBy(c => new { 
             CustomerId = c.EmployeeId, 
             c.Status })
         .ToDictionary(g => g.Key.CustomerId, g => g.Key.Status, g => g.ToList());

foreach (var item in dict[id][status])
{
    Console.WriteLine(item.CustomerId + " " + item.CustomerName); 
}

It looks completed but right now, I am having exceptions and the one is: 'Cannot convert to lambda expression to type System.Collections.Generic.IEComparer because it is not a delegate type' - ToDictionary(g => g.Key.CustomerId, g => g.Key.Status, g => g.ToList(). The other error in this line: var item in dict[id][status]. Is there any way to get rid of it and may be doing the wrong thing somewhere.

stuartd
  • 70,509
  • 14
  • 132
  • 163
user8512043
  • 1,043
  • 12
  • 20

3 Answers3

3

I would change your dictionary to something like this:

Dictionary<Tuple<int, string>, List<Employee>>

Because you want the int and the string to both be together and designate a key.

Also, the .ToDictionary call would look like this:

Dictionary<Tuple<int, string>, List<Employee>> dict = 
    employees.ToDictionary(g => 
        new Tuple<int, string>(g.Key.CustomerId, g.Key.Status), g => g);
Omar Himada
  • 2,540
  • 1
  • 14
  • 31
  • I am having exception here again - **g => g.ToList()** and it says - **Cannot convert to lambda expression to type > because it is not a delegate type** – user8512043 Sep 28 '17 at 13:24
  • It should be `g => g` not `g => g.ToList()` because "g" is already a `List` – Omar Himada Sep 28 '17 at 13:28
1

Why not use a tuple as a key?

Dictionary<Tuple<K1, K2>, V> d1;
Neo
  • 3,534
  • 2
  • 20
  • 32
0

Actually you can do that already without an additional class which is even limited to only two keys. Use a Lookup<TKey, TElement> which uses an anonymousy type as key:

var idStatusLookup = employees.ToLookup(x => new {x.EmployeeId, x.Status});
var matchingEmployees = idStatusLookup[new { EmployeeId = 1001, Status = "Active"}];

foreach (Employee emp in matchingEmployees)
{
    Console.WriteLine(emp.EmployeeId + " " + emp.EmployeeName);
}

A lookup is similar to a dictionary but it allows to have multiple keys and there is always a value, even for non-contained keys. How is that possible? By returning Enumerable.Empty<TValue> if the key is not contained and by returning a sequence of values for the same key.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • OK and thanks for the idea @Tim Schmelter. So does it mean in my way that I've tried should be avoided to use or have limitation. Never mind - I am just asking as I am not that expert in this. – user8512043 Sep 28 '17 at 13:34
  • @user8512043: well, you see how short the code above is compared to yours and it works. So why you want to create a new dictionary type that is limited to only two key-pairs instead of infinite? – Tim Schmelter Sep 28 '17 at 13:37
  • That's true @Tim Schmelter. I was just wondering if it is possible to do. Simple things are really preferable. Thanks again. – user8512043 Sep 28 '17 at 13:39
  • @TimSchmelter the `Lookup` is not for 2 keys and 1 value, but for 1 key and an arbitrary number of values. If OP wanted the key "Bob" to have the values "fish" and "cheese", a `Lookup` is fine. However, they want the keys "fish" and "cheese", when used together, to have the value "Bob". https://learn.microsoft.com/en-us/dotnet/api/system.linq.lookup-2?redirectedfrom=MSDN&view=net-7.0 – Blarghedy May 26 '23 at 12:20