merging two dictionaries but values adds up - algorithm

i have two dictionaries each of them has a string as key and an int as value i want to merge the two dictionaries but in case of duplicate keys the values get added up i had the idea of using loops to fill the 3rd dictionary but is there a simpler method that reduce time mainly
thank you in advance :D

You could use Linq:
using System;
using System.Collections.Generic;
using System.Linq;
class MainClass {
public static void Main(string[] args) {
IDictionary <string, int> dictA = new Dictionary <string, int>();
dictA.Add("Apple", 2);
dictA.Add("Banana", 1);
IDictionary <string, int> dictB = new Dictionary <string, int>();
dictB.Add("Apple", 1);
dictB.Add("Cherry", 5);
IDictionary <string, int> dictC = dictA.Concat(dictB)
.GroupBy(i => i.Key)
.ToDictionary(i => i.Key, i => i.Sum(j => j.Value));
foreach(KeyValuePair <string, int> kvp in dictC) {
Console.WriteLine("Key: {0}, Value: {1}", kvp.Key, kvp.Value);
}
}
}
Output:
Key: Apple, Value: 3
Key: Banana, Value: 1
Key: Cherry, Value: 5

Related

Grouped custom object with a list property

I have a list of customObject, I want to group the "CustomObject" by the List property of the CustomObject object.
public class CustomObject
{
public string Name { get; set; }
public List<string> List { get; set; }
public CustomObject(string name, List<string> list)
{
this.Name = name;
this.List = list;
}
}
.....................
List<CustomObject> listCustomObject = new List<CustomObject>()
{
new CustomObject("A", new List<string>(){ "1","2","3", "4"} ),
new CustomObject("B", new List<string>(){ "4","8","5"}),
new CustomObject("C", new List<string>(){ "5","1","2", "4"})
};
Desired results :
"A"/"C" => identical item in the list ("1", "2")
"A"/"B"/"C" => identical item in the list ("4")
"B"/"C" => identical item in the list ("5")
Using some extension methods, you can generate all combinations of the inputs having at least two members:
public static IEnumerable<IEnumerable<T>> AtLeastCombinations<T>(this IEnumerable<T> elements, int minK) => Enumerable.Range(minK, elements.Count()+1-minK).SelectMany(k => elements.Combinations(k));
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k) {
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
}
Now you can simply test each combination to see if they have any common elements:
var ans = listCustomObject.AtLeastCombinations(2)
.Select(c => new { CombinationNames = c.Select(co => co.Name).ToList(), CombinationIntersect = c.Select(co => co.List).Aggregate((sofar, coList) => sofar.Intersect(coList).ToList()) })
.Where(ci => ci.CombinationIntersect.Count > 0)
.ToList();

C# LINQ find duplicates in List

Using LINQ, from a List<int>, how can I retrieve a list that contains entries repeated more than once and their values?
The easiest way to solve the problem is to group the elements based on their value, and then pick a representative of the group if there are more than one element in the group. In LINQ, this translates to:
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => y.Key)
.ToList();
If you want to know how many times the elements are repeated, you can use:
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => new { Element = y.Key, Counter = y.Count() })
.ToList();
This will return a List of an anonymous type, and each element will have the properties Element and Counter, to retrieve the information you need.
And lastly, if it's a dictionary you are looking for, you can use
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.ToDictionary(x => x.Key, y => y.Count());
This will return a dictionary, with your element as key, and the number of times it's repeated as value.
Find out if an enumerable contains any duplicate :
var anyDuplicate = enumerable.GroupBy(x => x.Key).Any(g => g.Count() > 1);
Find out if all values in an enumerable are unique :
var allUnique = enumerable.GroupBy(x => x.Key).All(g => g.Count() == 1);
Another way is using HashSet:
var hash = new HashSet<int>();
var duplicates = list.Where(i => !hash.Add(i));
If you want unique values in your duplicates list:
var myhash = new HashSet<int>();
var mylist = new List<int>(){1,1,2,2,3,3,3,4,4,4};
var duplicates = mylist.Where(item => !myhash.Add(item)).Distinct().ToList();
Here is the same solution as a generic extension method:
public static class Extensions
{
public static IEnumerable<TSource> GetDuplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IEqualityComparer<TKey> comparer)
{
var hash = new HashSet<TKey>(comparer);
return source.Where(item => !hash.Add(selector(item))).ToList();
}
public static IEnumerable<TSource> GetDuplicates<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
return source.GetDuplicates(x => x, comparer);
}
public static IEnumerable<TSource> GetDuplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
return source.GetDuplicates(selector, null);
}
public static IEnumerable<TSource> GetDuplicates<TSource>(this IEnumerable<TSource> source)
{
return source.GetDuplicates(x => x, null);
}
}
To find the duplicate values only:
var duplicates = list.GroupBy(x => x.Key).Where(g => g.Count() > 1);
E.g.
var list = new[] {1,2,3,1,4,2};
GroupBy will group the numbers by their keys and will maintain the count (number of times it is repeated) with it. After that, we are just checking the values which have repeated more than once.
To find the unique values only:
var unique = list.GroupBy(x => x.Key).Where(g => g.Count() == 1);
E.g.
var list = new[] {1,2,3,1,4,2};
GroupBy will group the numbers by their keys and will maintain the count (number of times it repeated) with it. After that, we are just checking the values who have repeated only once means are unique.
You can do this:
var list = new[] {1,2,3,1,4,2};
var duplicateItems = list.Duplicates();
With these extension methods:
public static class Extensions
{
public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
var grouped = source.GroupBy(selector);
var moreThan1 = grouped.Where(i => i.IsMultiple());
return moreThan1.SelectMany(i => i);
}
public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source)
{
return source.Duplicates(i => i);
}
public static bool IsMultiple<T>(this IEnumerable<T> source)
{
var enumerator = source.GetEnumerator();
return enumerator.MoveNext() && enumerator.MoveNext();
}
}
Using IsMultiple() in the Duplicates method is faster than Count() because this does not iterate the whole collection.
I created a extention to response to this you could includ it in your projects, I think this return the most case when you search for duplicates in List or Linq.
Example:
//Dummy class to compare in list
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public Person(int id, string name, string surname)
{
this.Id = id;
this.Name = name;
this.Surname = surname;
}
}
//The extention static class
public static class Extention
{
public static IEnumerable<T> getMoreThanOnceRepeated<T>(this IEnumerable<T> extList, Func<T, object> groupProps) where T : class
{ //Return only the second and next reptition
return extList
.GroupBy(groupProps)
.SelectMany(z => z.Skip(1)); //Skip the first occur and return all the others that repeats
}
public static IEnumerable<T> getAllRepeated<T>(this IEnumerable<T> extList, Func<T, object> groupProps) where T : class
{
//Get All the lines that has repeating
return extList
.GroupBy(groupProps)
.Where(z => z.Count() > 1) //Filter only the distinct one
.SelectMany(z => z);//All in where has to be retuned
}
}
//how to use it:
void DuplicateExample()
{
//Populate List
List<Person> PersonsLst = new List<Person>(){
new Person(1,"Ricardo","Figueiredo"), //fist Duplicate to the example
new Person(2,"Ana","Figueiredo"),
new Person(3,"Ricardo","Figueiredo"),//second Duplicate to the example
new Person(4,"Margarida","Figueiredo"),
new Person(5,"Ricardo","Figueiredo")//third Duplicate to the example
};
Console.WriteLine("All:");
PersonsLst.ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
/* OUTPUT:
All:
1 -> Ricardo Figueiredo
2 -> Ana Figueiredo
3 -> Ricardo Figueiredo
4 -> Margarida Figueiredo
5 -> Ricardo Figueiredo
*/
Console.WriteLine("All lines with repeated data");
PersonsLst.getAllRepeated(z => new { z.Name, z.Surname })
.ToList()
.ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
/* OUTPUT:
All lines with repeated data
1 -> Ricardo Figueiredo
3 -> Ricardo Figueiredo
5 -> Ricardo Figueiredo
*/
Console.WriteLine("Only Repeated more than once");
PersonsLst.getMoreThanOnceRepeated(z => new { z.Name, z.Surname })
.ToList()
.ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
/* OUTPUT:
Only Repeated more than once
3 -> Ricardo Figueiredo
5 -> Ricardo Figueiredo
*/
}
there is an answer but i did not understand why is not working;
var anyDuplicate = enumerable.GroupBy(x => x.Key).Any(g => g.Count() > 1);
my solution is like that in this situation;
var duplicates = model.list
.GroupBy(s => s.SAME_ID)
.Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
doSomething();
}
Complete set of Linq to SQL extensions of Duplicates functions checked in MS SQL Server. Without using .ToList() or IEnumerable. These queries executing in SQL Server rather than in memory.. The results only return at memory.
public static class Linq2SqlExtensions {
public class CountOfT<T> {
public T Key { get; set; }
public int Count { get; set; }
}
public static IQueryable<TKey> Duplicates<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
=> source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(s => s.Key);
public static IQueryable<TSource> GetDuplicates<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
=> source.GroupBy(groupBy).Where(w => w.Count() > 1).SelectMany(s => s);
public static IQueryable<CountOfT<TKey>> DuplicatesCounts<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
=> source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(y => new CountOfT<TKey> { Key = y.Key, Count = y.Count() });
public static IQueryable<Tuple<TKey, int>> DuplicatesCountsAsTuble<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
=> source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(s => Tuple.Create(s.Key, s.Count()));
}
Linq query:
var query = from s2 in (from s in someList group s by new { s.Column1, s.Column2 } into sg select sg) where s2.Count() > 1 select s2;
This More simple way without use Groups just get the District elements and then iterate over them and check their count in the list if their count is >1 this mean it appear more than 1 item so add it to Repeteditemlist
var mylist = new List<int>() { 1, 1, 2, 3, 3, 3, 4, 4, 4 };
var distList= mylist.Distinct().ToList();
var Repeteditemlist = new List<int>();
foreach (var item in distList)
{
if(mylist.Count(e => e == item) > 1)
{
Repeteditemlist.Add(item);
}
}
foreach (var item in Repeteditemlist)
{
Console.WriteLine(item);
}
Expected OutPut:
1
3
4
Just an another approach:
For just HasDuplicate:
bool hasAnyDuplicate = list.Count > list.Distinct().Count;
For duplicate values
List<string> duplicates = new List<string>();
duplicates.AddRange(list);
list.Distinct().ToList().ForEach(x => duplicates.Remove(x));
// for unique duplicate values:
duplicates.Distinct():
All the GroupBy answers are the simplest but won't be the most efficient. They're especially bad for memory performance as building large inner collections has allocation cost.
A decent alternative is HuBeZa's HashSet.Add based approach. It performs better.
If you don't care about nulls, something like this is the most efficient (both CPU and memory) as far as I can think:
public static IEnumerable<TProperty> Duplicates<TSource, TProperty>(
this IEnumerable<TSource> source,
Func<TSource, TProperty> duplicateSelector,
IEqualityComparer<TProperty> comparer = null)
{
comparer ??= EqualityComparer<TProperty>.Default;
Dictionary<TProperty, int> counts = new Dictionary<TProperty, int>(comparer);
foreach (var item in source)
{
TProperty property = duplicateSelector(item);
counts.TryGetValue(property, out int count);
switch (count)
{
case 0:
counts[property] = ++count;
break;
case 1:
counts[property] = ++count;
yield return property;
break;
}
}
}
The trick here is to avoid additional lookup costs once the duplicate count has reached 1. Of course you could keep updating the dictionary with count if you also want the number of duplicate occurrences for each item. For nulls, you just need some additional handling there, that's all.
Remove duplicates by key
myTupleList = myTupleList.GroupBy(tuple => tuple.Item1).Select(group => group.First()).ToList();

Populate dictionary from enum

I have the following enum:
public enum LifeCycle
{
Pending = 0,
Approved = 1,
Rejected = 2,
}
And I want to create
Dictionary<int, string> LifeCycleDict;
from the enum value and its toString
Is there a way to do it with linq?
(The equivelant to java's enum.values )
Thanks.
Dictionary<int, string> LifeCycleDict = Enum.GetNames(typeof(LifeCycle))
.ToDictionary(Key => (int)Enum.Parse(typeof(LifeCycle), Key), value => value);
OR
Dictionary<int, string> LifeCycleDict = Enum.GetValues(typeof(LifeCycle)).Cast<int>()
.ToDictionary(Key => Key, value => ((LifeCycle)value).ToString());
OR
Dictionary<int, string> LifeCycleDict = Enum.GetValues(typeof(LifeCycle)).Cast<LifeCycle>()
.ToDictionary(t => (int)t, t => t.ToString());

Calculate difference from previous item with LINQ

I'm trying to prepare data for a graph using LINQ.
The problem that i cant solve is how to calculate the "difference to previous.
the result I expect is
ID= 1, Date= Now, DiffToPrev= 0;
ID= 1, Date= Now+1, DiffToPrev= 3;
ID= 1, Date= Now+2, DiffToPrev= 7;
ID= 1, Date= Now+3, DiffToPrev= -6;
etc...
Can You help me create such a query ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public class MyObject
{
public int ID { get; set; }
public DateTime Date { get; set; }
public int Value { get; set; }
}
class Program
{
static void Main()
{
var list = new List<MyObject>
{
new MyObject {ID= 1,Date = DateTime.Now,Value = 5},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(1),Value = 8},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(2),Value = 15},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(3),Value = 9},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(4),Value = 12},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(5),Value = 25},
new MyObject {ID= 2,Date = DateTime.Now,Value = 10},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(1),Value = 7},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(2),Value = 19},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(3),Value = 12},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(4),Value = 15},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(5),Value = 18}
};
Console.WriteLine(list);
Console.ReadLine();
}
}
}
One option (for LINQ to Objects) would be to create your own LINQ operator:
// I don't like this name :(
public static IEnumerable<TResult> SelectWithPrevious<TSource, TResult>
(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> projection)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
TSource previous = iterator.Current;
while (iterator.MoveNext())
{
yield return projection(previous, iterator.Current);
previous = iterator.Current;
}
}
}
This enables you to perform your projection using only a single pass of the source sequence, which is always a bonus (imagine running it over a large log file).
Note that it will project a sequence of length n into a sequence of length n-1 - you may want to prepend a "dummy" first element, for example. (Or change the method to include one.)
Here's an example of how you'd use it:
var query = list.SelectWithPrevious((prev, cur) =>
new { ID = cur.ID, Date = cur.Date, DateDiff = (cur.Date - prev.Date).Days) });
Note that this will include the final result of one ID with the first result of the next ID... you may wish to group your sequence by ID first.
Use index to get previous object:
var LinqList = list.Select(
(myObject, index) =>
new {
ID = myObject.ID,
Date = myObject.Date,
Value = myObject.Value,
DiffToPrev = (index > 0 ? myObject.Value - list[index - 1].Value : 0)
}
);
In C#4 you can use the Zip method in order to process two items at a time. Like this:
var list1 = list.Take(list.Count() - 1);
var list2 = list.Skip(1);
var diff = list1.Zip(list2, (item1, item2) => ...);
Modification of Jon Skeet's answer to not skip the first item:
public static IEnumerable<TResult> SelectWithPrev<TSource, TResult>
(this IEnumerable<TSource> source,
Func<TSource, TSource, bool, TResult> projection)
{
using (var iterator = source.GetEnumerator())
{
var isfirst = true;
var previous = default(TSource);
while (iterator.MoveNext())
{
yield return projection(iterator.Current, previous, isfirst);
isfirst = false;
previous = iterator.Current;
}
}
}
A few key differences... passes a third bool parameter to indicate if it is the first element of the enumerable. I also switched the order of the current/previous parameters.
Here's the matching example:
var query = list.SelectWithPrevious((cur, prev, isfirst) =>
new {
ID = cur.ID,
Date = cur.Date,
DateDiff = (isfirst ? cur.Date : cur.Date - prev.Date).Days);
});
Further to Felix Ungman's post above, below is an example of how you can achieve the data you need making use of Zip():
var diffs = list.Skip(1).Zip(list,
(curr, prev) => new { CurrentID = curr.ID, PreviousID = prev.ID, CurrDate = curr.Date, PrevDate = prev.Date, DiffToPrev = curr.Date.Day - prev.Date.Day })
.ToList();
diffs.ForEach(fe => Console.WriteLine(string.Format("Current ID: {0}, Previous ID: {1} Current Date: {2}, Previous Date: {3} Diff: {4}",
fe.CurrentID, fe.PreviousID, fe.CurrDate, fe.PrevDate, fe.DiffToPrev)));
Basically, you are zipping two versions of the same list but the first version (the current list) begins at the 2nd element in the collection, otherwise a difference would always differ the same element, giving a difference of zero.
I hope this makes sense,
Dave
Yet another mod on Jon Skeet's version (thanks for your solution +1). Except this is returning an enumerable of tuples.
public static IEnumerable<Tuple<T, T>> Intermediate<T>(this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
T previous = iterator.Current;
while (iterator.MoveNext())
{
yield return new Tuple<T, T>(previous, iterator.Current);
previous = iterator.Current;
}
}
}
This is NOT returning the first because it's about returning the intermediate between items.
use it like:
public class MyObject
{
public int ID { get; set; }
public DateTime Date { get; set; }
public int Value { get; set; }
}
var myObjectList = new List<MyObject>();
// don't forget to order on `Date`
foreach(var deltaItem in myObjectList.Intermediate())
{
var delta = deltaItem.Second.Offset - deltaItem.First.Offset;
// ..
}
OR
var newList = myObjectList.Intermediate().Select(item => item.Second.Date - item.First.Date);
OR (like jon shows)
var newList = myObjectList.Intermediate().Select(item => new
{
ID = item.Second.ID,
Date = item.Second.Date,
DateDiff = (item.Second.Date - item.First.Date).Days
});
Here is the refactored code with C# 7.2 using the readonly struct and the ValueTuple (also struct).
I use Zip() to create (CurrentID, PreviousID, CurrDate, PrevDate, DiffToPrev) tuple of 5 members. It is easily iterated with foreach:
foreach(var (CurrentID, PreviousID, CurrDate, PrevDate, DiffToPrev) in diffs)
The full code:
public readonly struct S
{
public int ID { get; }
public DateTime Date { get; }
public int Value { get; }
public S(S other) => this = other;
public S(int id, DateTime date, int value)
{
ID = id;
Date = date;
Value = value;
}
public static void DumpDiffs(IEnumerable<S> list)
{
// Zip (or compare) list with offset 1 - Skip(1) - vs the original list
// this way the items compared are i[j+1] vs i[j]
// Note: the resulting enumeration will include list.Count-1 items
var diffs = list.Skip(1)
.Zip(list, (curr, prev) =>
(CurrentID: curr.ID, PreviousID: prev.ID,
CurrDate: curr.Date, PrevDate: prev.Date,
DiffToPrev: curr.Date.Day - prev.Date.Day));
foreach(var (CurrentID, PreviousID, CurrDate, PrevDate, DiffToPrev) in diffs)
Console.WriteLine($"Current ID: {CurrentID}, Previous ID: {PreviousID} " +
$"Current Date: {CurrDate}, Previous Date: {PrevDate} " +
$"Diff: {DiffToPrev}");
}
}
Unit test output:
// the list:
// ID Date
// ---------------
// 233 17-Feb-19
// 122 31-Mar-19
// 412 03-Mar-19
// 340 05-May-19
// 920 15-May-19
// CurrentID PreviousID CurrentDate PreviousDate Diff (days)
// ---------------------------------------------------------
// 122 233 31-Mar-19 17-Feb-19 14
// 412 122 03-Mar-19 31-Mar-19 -28
// 340 412 05-May-19 03-Mar-19 2
// 920 340 15-May-19 05-May-19 10
Note: the struct (especially readonly) performance is much better than that of a class.
Thanks #FelixUngman and #DavidHuxtable for their Zip() ideas!

LINQ Convert Dictionary to Lookup

I have a variable of type Dictionary<MyType, List<MyOtherType>>
I want to convert it to a Lookup<MyType, MyOtherType>.
I wanted to use Lambda functions to first, flatten the dictionary and then convert this to Lookup using the ToLookup(). I got stuck with the dictionary. I thought about using SelectMany but can't get it working. Anyone has got an idea how to do it?
Same as Jon's method, but avoiding the creation of an anonymous type:
var lookup = dictionary
.SelectMany(p => p.Value, Tuple.Create)
.ToLookup(p => p.Item1.Key, p => p.Item2);
How about:
var lookup = dictionary.SelectMany(pair => pair.Value,
(pair, Value) => new { pair.Key, Value })
.ToLookup(pair => pair.Key, pair => pair.Value);
It does feel like a little bit of a waste doing this when the dictionary already has all the information grouped appropriately, but I can't see a simple way round that. Of course you could implement ILookup<TKey, TValue> yourself with a wrapper around the dictionary...
Already a few answers here, but putting this here for reference.
This flips a dictionary with a list of values, to having those values as the keys of look up list.
var myLookup = myDict.SelectMany(p => p.Value,
(pair, id) => Tuple.Create(id, pair.Key))
.ToLookup(p => p.Item1, p => p.Item2);
Annotated
var myLookup = myDict.SelectMany(
// specify that the select many is to be based off the Value which is a list of items
p => p.Value,
// Using the individual items from that list, create a tuple of that item and the dictionary key it was under
(pair, id) => Tuple.Create(id, pair.Key))
// use the item as the lookup key, and put the original dictionary key (that
// had that value underneath them) in the list of lookup values.
.ToLookup(p => p.Item1, p => p.Item2);
Not an answer for the question, but I think this is related information and should be posted here.
There is some edge cases you should take into account. All of them about items of dictionary, which have key, but don't have value.
This is expected behavior. Dictionary and Lookup designed for different purposes.
var dic = new Dictionary<bool, IEnumerable<bool?>> { [true] = null };
var lookup = dic.ToLookup();
Assert.AreEqual(1, dic.Count);
Assert.AreEqual(0, lookup.Count);
Assert.IsTrue(dic.ContainsKey(true));
Assert.IsFalse(lookup.Contains(true));
Assert.IsFalse(dic.ContainsKey(false));
Assert.IsFalse(lookup.Contains(false));
dic[false] -> Exception
lookup[false] -> bool?[0]
Late to the party but I think this should work, without needing to enumerate everything again and create temporary tuples/anonymous types.
public static ILookup<TKey, TElement> ToLookup<TKey, TElement>(
this IEnumerable<TKey> keys,
Func<TKey, IEnumerable<TElement>> selector)
{
return new ManualLookup<TKey, TElement>(keys, selector);
}
private class ManualLookup<TKey, TElement> : ILookup<TKey, TElement>
{
private IEnumerable<TKey> _keys;
private Func<TKey, IEnumerable<TElement>> _selector;
public ManualLookup(IEnumerable<TKey> keys, Func<TKey, IEnumerable<TElement>> selector)
{
_keys = keys;
_selector = selector;
}
public IEnumerable<TElement> this[TKey key] => _selector(key);
public int Count => _keys.Count();
public bool Contains(TKey key) => _keys.Contains(key);
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator() => _keys
.Select(key => new ManualGrouping<TKey, TElement>(key, _selector(key)))
.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
private class ManualGrouping<TKey, TElement> : IGrouping<TKey, TElement>
{
private TKey _key;
private IEnumerable<TElement> _enumerable;
public ManualGrouping(TKey key, IEnumerable<TElement> enumerable)
{
_key = key;
_enumerable = enumerable;
}
public TKey Key => _key;
public IEnumerator<TElement> GetEnumerator() => _enumerable.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Then you can do something like:
Dictionary<MyType, List<MyOtherType>> dictionary;
return dictionary.Keys.ToLookup(key =>
{
if (dictionary.TryGetValue(key, out var list)
{
return list;
}
return Enumerable.Empty<MyOtherType>();
});

Resources