Order Dictionary from Keys from another Dictionary - c#

I have two dictionaries:
Dictionary<string, Tuple<T, T>> dict1
Dictionary<Enum, Tuple<string, DateTime, DateTime>> dict2
The string value in the first item in the tuple is sometimes equal to the key in the first dictionary. I would like to sort the second dictionary by the enum value and then sort the first dictionary based on the order that dict2 has. How would I be able to do that while retaining the other keys that might be in dict1? I'm able to get as far as sorting the dictionary.
var positions = new Dictionary<Enum, string>();
//This foreach loop can be simplified by a linq expression.
foreach (var position in dict2)
{
var isFound = dict1.Any(x => x.Key == position.Value.Item1.Value);
if (isFound)
{
clubPositions.Add(position.Key, position.Value.Item1.Value);
}
}
var sortedPositions = positions.OrderByDescending(x => x.Key);

You are looking for the SortedDictionary documented on MSDN here. You will need to instantiate them as such:
var dict1 = new SortedDictionary<string, Tuple<DateTime, DateTime>>();
var dict2 = new SortedDictionary<Enum, Tuple<string, DateTime, DateTime>>();
Once you have the sorted dictionaries instances you can use LINQ to map or filter or do whatever else is desired on them. When items are added to the dictionaries they are automatically sorted based on either the ICompare<T> for the given TKey (or the default IComparer if not explicitly provided).
One downside is the fact that this sorts based on the key alone, there is no concept of sorting based on values.

The way I understand the question is not to sort the first dictionary, but to be able to iterate its elements in order defined by the second dictionary keys. If that's correct, the following should do the trick:
var orderedKeys = new HashSet<string>(dict2.OrderBy(e => e.Key).Select(e => e.Value.Item1));
var orderedEntries = orderedKeys.Where(dict1.ContainsKey)
.Select(key => new KeyValuePair<string, Tuple<T, T>>(key, dict1[key]))
.Concat(dict1.Where(e => !orderedKeys.Contains(e.Key)));
Note that it will put the entries that has no corresponding key last in the order.

You can not sort dictionary as it uses hash algorithm for constant search.

A regular dictionary cannot be sorted. You could use a SortedDictionary, it exists for this exact purpose.

Related

Convert dictionary with List to IEnumerable

I have a dictionary:
Dictionary<String, List<Foo>> test = new Dictionary<String, List<Foo>>();
I then populate this dictionary hence why I need the list so I can call Add(). My problem is the function needs to return:
Dictionary<String, IEnumerable<Foo>>
Is there any easy way to do this without doing the obvious and looping through my original dictionary and doing it manually?
return dictionary.ToDictionary(x => x.Key,x => x.Value.AsEnumerable())
It's more efficient and easier to use the List<Foo> to add things but add it to a Dictionary<String, IEnumerable<Foo>>. That's no problem since List<Foo> implements IEnumerable<Foo>, it's not even necessary to cast.
So something like this(pseudo code):
var test = new Dictionary<String, IEnumerable<Foo>>();
foreach(var x in something)
{
var list = new List<Foo>();
foreach(var y in x.SomeCollection)
list.Add(y.SomeProperty);
test.Add(x.KeyProperty, list); // works since List<T> is also an IEnumerable<T>
}
I tried this route as well, converting Dictionary<string, List<Foo>> to a ReadOnlyDictionary<string, IEnumerable<Foo>>. While I was trying to convert to a read-only dictionary, the whole purpose of converting a List to IEnumerable is to make a read only collection. The problem with the OP's approach is:
Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();
errors["foo"] = new List<string>() { "You can't do this" };
Dictionary<string, IEnumerable<string>> readOnlyErrors = // convert errors...
readOnlyErrors["foo"] = new List<string>() { "I'm not actually read-only!" };
The appearance of IEnumerable<Foo> makes you think this is read only and safe, when in fact it is not. After reading the question LINQ Convert Dictionary to Lookup a Lookup object is more appropriate, because it allows you to:
Associate one key with multiple values
You cannot overwrite a key with a new value
// This results in a compiler error
lookUp["foo"] = new List<Foo>() { ... };
The "multiple values" are already defined as IEnumerable<T>
You can still use the same outer and inner loop algorithm to extract individual values:
ILookup<string, string> lookup = // Convert to lookup
foreach (IGrouping<string, string> grouping in lookup)
{
Console.WriteLine(grouping.Key + ":");
foreach (string item in grouping)
{
Console.WriteLine(" item: " + item);
}
}
Convert Dictionary<string, List<Foo>> to ILookup<string, Foo>
It's a quick two-liner:
Dictionary<string, List<Foo>> foos = // Create and populate 'foos'
ILookup<string, Foo> lookup = foos.SelectMany(item => item.Value, Tuple.Create)
.ToLookup(p => p.Item1.Key, p => p.Item2);
Now you can use the same two-step loop as you would have with a Dictionary<string, IEnumerable<Foo>>:
foreach (IGrouping<string, Foo> grouping in lookup)
{
string key = grouping.Key;
foreach (Foo foo in grouping)
{
// Do stuff with key and foo
}
}
Source: LINQ Convert Dictionary to Lookup
Converting to another Dictionary with an IEnumerable value is like trying to stuff a square peg into a round hole. The more appropriate, and safe way (from an object-oriented standpoint) is to convert your read/write Dictionary to a Lookup. This gives you the true intended safety of an object that is read-only (except for the Foo items, which might not be immutable).
I would go so far as to say that most times when a ReadOnlyDictionary is used, you could use ILookup and get the same functionality.

What type is the best for loose numerically-indexed lists in C#?

What I need is something like an array but letting me to assign an element to whatever an index at any time and check if there is already a value assigned to particular index approximately like
MyArray<string> a = new MyArray<string>();
a[10] = "ten";
bool isTheFifthElementDefined = a[5] != null; // false
Perhaps Dictionary<int, string> with its ContainsKey method could do, but isn't there a more appropriate data structure if I want an ordered collection with numeric keys only?
I am also going to need to iterate through the defined elements (with foreach or linq preferably) accessing both the value and the key of current element.
As you mentioned Dictionary seems more appropriate for this.But you can do it with generic lists,for example, when you are creating your list you can specify an element count,and you can give a default temporary value for all your elements.
List<string> myList = new List<string>(Enumerable.Repeat("",5000));
myList[2300] = "bla bla bla..";
For int:
List<int> myList = new List<int>(Enumerable.Repeat(0,5000));
For custom type:
List<MyClass> myList = new List<MyClass>(Enumerable.Repeat(new MyClass(), 100));
Ofcourse It is not the best solution...
Note: Also you can use SortedList instead of Dictionary if you want an ordered collection by keys:
SortedList<TKey, TValue> : Represents a collection of key/value pairs that are sorted by key based on the associated IComparer implementation.
If you need key/value pairs you cannot use a list, you'll need a Dictionary.
The implementation is pretty snappy so don't be too afraid about performance (as long as you don't put too much values in it).
You can iterate over it with
foreach(KeyValuePair<int, string> kvp in dict)
{
}
If you need to order it you can use a list:
List<int> ordered = new List(dict.Keys);
ordered.Sort();
foreach(int key in ordered)
{
}

A dictionary with multiple entries with the same key [duplicate]

This question already has answers here:
C# dictionary - one key, many values
(15 answers)
Closed 8 years ago.
I need a Dictionary like object that can store multiple entries with the same key. Is this avaliable as a standard collection, or do I need to roll my own?
To clarify, I want to be able to do something like this:
var dict = new Dictionary<int, String>();
dict.Add(1, "first");
dict.Add(1, "second");
foreach(string x in dict[1])
{
Console.WriteLine(x);
}
Output:
first
second
In .NET 3.5 you can use a Lookup instead of a Dictionary.
var items = new List<KeyValuePair<int, String>>();
items.Add(new KeyValuePair<int, String>(1, "first"));
items.Add(new KeyValuePair<int, String>(1, "second"));
var lookup = items.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
foreach (string x in lookup[1])
{
Console.WriteLine(x);
}
The Lookup class is immutable. If you want a mutable version you can use EditableLookup from MiscUtil.
I would recommend doing something like this:
var dict = new Dictionary<int, HashSet<string>>();
dict.Add(1, new HashSet<string>() { "first", "second" });
Dictionary<T,K> does not support such behavior and there's no collection in the base class library providing such behavior. The easiest way is to construct a composite data structure like this:
var data = new Dictionary<int, List<string>>();
As the second parameter you should use a collection which provides the qualities you are looking for, i.e. stable order ⇒ List<T>, fast access HashSet<T>, etc.
You definitely want to use NameValueCollection:
using System.Collections.Specialized;
NameValueCollection nvc = new NameValueCollection();
nvc.Add("pets", "Dog");
nvc.Add("pets", "Rabbit");
Console.WriteLine(nvc["pets"]);
//returns Dog,Rabbit
What you're looking for isn't actually a Dictionary in the traditional sense (see Associative Array).
There's no class, as far as I'm aware, that offers this in the framework (System.Linq.Lookup doesn't expose a constructor), but you could create a class yourself that implements ILookup<TKey, TElement>
You could perhaps use a Dictionary on your primary key, in which each element is a List or other collection on your secondary key. To add an item to your data structure, see if the primary key exists. If not, create a new single-item list with your Value and store it in the dictionary. If the primary key does exist, add your Value to the list that's in the dictionary.

Using LINQ to find all keys from one collection that are not in another?

I'm trying to locate all the keys in one Dictionary that are not in another Dictionary. Obviously, I can do this using a nested loop, but I'm trying to learn LINQ at the moment and I was wondering if I might use it to accomplish this task?
Here's what I have so far:
Dictionary<string, List<string>> DBtables = this.CollectTableListings();
var generic = from Dictionary<string,List<string>> tab
in DBtables
where !_tables.ContainsKey(???)
select tab;
Any idea what should go in place of the question marks (or perhaps instead of the entire where clause)?
You can do:
var resultKeys = DBTables.Keys.Except( _tables.Keys );
The Except() method is essentially the same as the minus operations in SQL - it returns all items from the first collection excluding those in the second. Since dictionaries expose their keys, you can compute their difference that way.
The Except() operator uses the default equality for the type, but there is also an overload which allows you to specify your own IEqualityComparer to override the semantics of how to compare values. In your example, you probably don't need that - but it's nice to know it there.
Dictionary<string, List<string>> dictOne = ...
Dictionary<string, List<string>> dictTwo = ...
var missingKeys = dictOne.Keys.Where(x => !dictTwo.ContainsKey(x));
Dictionary<string, List<string>> dictionary = this.CollectTableListings();
Dictionary<string, List<string>> otherDictionary = getOtherTable();
var keys = from key in dictionary.Keys
where !otherDictionary.Keys.Contains(key)
select key;
(But LBuskin's answer is much better)
have a look at the Except extension method. HTH.
If you wanted to use query syntax I would do something akin to below:
var keys = from d1 in dictionary1
select d1.Key;
var items = from d2 in dictionary2
where d2.Key in keys
select d2;
foreach(var item in items)
{
}

C# - Remove a item from list of KeyValuePair

How can I remove a item from list of KeyValuePair?
If you have both the key and the value you can do the following
public static void Remove<TKey,TValue>(
this List<KeyValuePair<TKey,TValue>> list,
TKey key,
TValue value) {
return list.Remove(new KeyValuePair<TKey,TValue>(key,value));
}
This works because KeyValuePair<TKey,TValue> does not override Equality but is a struct. This means it uses the default value equality. This simply compares the values of the fields to test for equality. So you simply need to create a new KeyValuePair<TKey,TValue> instance with the same fields.
EDIT
To respond to a commenter, what value does an extension method provide here?
Justification is best seen in code.
list.Remove(new KeyValuePair<int,string>(key,value));
list.Remove(key,value);
Also in the case where either the key or value type is an anonymous type, an extension method is required.
EDIT2
Here's a sample on how to get KeyValuePair where one of the 2 has an anonymous type.
var map =
Enumerable.Range(1,10).
Select(x => new { Id = x, Value = x.ToString() }).
ToDictionary(x => x.Id);
The variable map is a Dicitonary<TKey,TValue> where TValue is an anonymous type. Enumerating the map will produce a KeyValuePair with the TValue being the same anonymous type.
Here are a few examples of removing an item from a list of KeyValuePair:
// Remove the first occurrence where you have key and value
items.Remove(new KeyValuePair<int, int>(0, 0));
// Remove the first occurrence where you have only the key
items.Remove(items.First(item => item.Key.Equals(0)));
// Remove all occurrences where you have the key
items.RemoveAll(item => item.Key.Equals(0));
EDIT
// Remove the first occurrence where you have the item
items.Remove(items[0]);
To remove all items in the list by key:
myList.RemoveAll(x => x.Key.Equals(keyToRemove));
Should be able to use the .Remove(), .RemoveAt(), or one of the other methods.
List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
KeyValuePair<string, string> kvp = list[i];
list.Remove(kvp);
or
list.Remove(list[1]);
You must obtain a reference to the object you want to remove - that's why I found the item I'm looking for and assigned to a KeyValuePair - since you're telling it to remove a specific item.
A better solution might be to use a dictionary:
Dictionary<string, string> d = new Dictionary<string, string>();
if (d.ContainsKey("somekey")) d.Remove("somekey");
This allows you to remove by the key value instead of having to deal with the list not being indexed by the key.
Edit you may not have to get a KeyValuePair reference first. Still, a dictionary is probably a better way to go.

Categories