How can I Remove items from dictionary using lambda expression - c#

I am not into LINQ solutions,
I am using simple predicat to determine if the key should be removed,
For example if the dictionary is construct like Dictionary<int, int>,
so how should I remove all the entries with negative data
I am prefer to use the same dictionary, not to create new one,
I don't have preformance issues
Is there a way to do it, without using LINQ, but using Lambda expressions?
I didn't want solutions in LINQ because no one is using them in my project, didn't want to be the first.., but because I saw the LINQ solutions look better, I will use them them..

The simplest way is probably to create a new dictionary, if that's okay for you:
var newDictionary = oldDictionary.Where(pair => pair.Value >= 0)
.ToDictionary(pair => pair.Key,
pair => pair.Value);
If you have to mutate the existing dictionary (e.g. because several other objects have reference to the same dictionary) you'd need to build a list of keys to remove, then remove them afterwards:
var toRemove = dictionary.Where(pair => pair.Value < 0)
.Select(pair => pair.Key)
.ToList();
foreach (var key in toRemove)
{
dictionary.Remove(key);
}
EDIT: I've just noticed the first sentence: "I am not into LINQ solutions". If that means you don't want to use a LINQ solution, here's the by-hand version:
List<int> toRemove = new List<int>();
foreach (KeyValuePair<int, int> pair in dictionary)
{
if (pair.Value < 0)
{
toRemove.Add(pair.Key);
}
}
foreach (var key in toRemove)
{
dictionary.Remove(key);
}
... but if you can use LINQ, I'd encourage you do. My second solution is equivalent to the "by-hand" version, but more readable IMO.

By merely using lambda expression:
foreach (var i in myDict.Where(d => (d.Value < 0 || d.key <0)).ToList() )
{
myDict.Remove(i.Key);
}

var toRemove = dict.Keys.Where(predicate).ToArray();
foreach (var key in toRemove) {
dict.Remove(key);
}

Well if you add
namespace MMExtensions
{
public static class DictionaryExtensions
{
public delegate bool Predicate<TKey, TValue>(KeyValuePair<TKey, TValue> d);
[MethodImpl(MethodImplOptions.Synchronized)]
public static void Filter<TKey, TValue>(
this Dictionary<TKey, TValue> hashtable, Predicate<TKey, TValue> p)
{
foreach (KeyValuePair<TKey, TValue> value in hashtable.ToList().Where(value => !p(value)))
hashtable.Remove(value.Key);
}
}
}
And you had some dataset as dictionary:
Dictionary<string, int> d =
new Dictionary<string, int> {{"v", -3}, {"val1", 1}, {"val2", 2}};
Then you could use:
d.Filter(delegate(KeyValuePair<string, int> kv) { return kv.Value >= 0; });
d.Filter(kv => kv.Value >= 0);// or as lambda

Do you want to remove the items from that dictionary, or are you happy to use a new dictionary without those items included?
var d = new Dictionary<int,int>();
var newDict = d.Where(entry => entry.Value >= 0).ToDictionary(entry => entry.Key, entry => entry.Value);

Easiest one:
Dictionary<long, long> dict...
Dictionary<long, long> result = dict.Were(x => x.Value >= 0).ToDictionary(x => x.Key, x => x.Value);
Or just loop over all in 'for' in reverse order and remove invalid ones.

I know you said you are not into Linq, but I could not contain myself with the following solution, plus it is still useful if you read the title of your question. This is probably the most elegant solution to your problem:
dictionary.Where(pair => pair.Value < 0)
.Select(pair => {
dictionary.Remove(pair.Key);
return pair.Key;
});

Related

Efficient way to create a new list based of the differences in values in 2 dictionaries?

I currently have 2 strings that are formatted as an XML that are later converted into dictionaries for comparison.
So, I have a 2 Dictionary<string, object>, dict1 and dict2, that I need to compare. I need to:
Add the key to a list of strings if the values of these two dictionaries do not match
Add the key of dict2 to the list if dict1 does not contain this key
Currently, I have a simple foreach loop
foreach (string propName in dict2.Keys)
{
string oldDictValue;
string newDicValue = dict1[propName].ToString();
if (dict1.ContainsKey(propName))
{
oldDictValue = dict2[propName].ToString();
if (oldDictValue != newDicValue)
list.Add(propName);
}
else
{
list.Add(propName);
}
}
I would like to a faster solution to this problem if possible?
I don't claim that this is any faster, but it should be on par and it's less code:
List<string> list =
dict2
.Keys
.Where(k => !(dict1.ContainsKey(k) && dict1[k].Equals(dict2[k])))
.ToList();
I did do some testing with this:
List<string> list =
dict2
.Keys
.AsParallel()
.Where(k => !(dict1.ContainsKey(k) && dict1[k].Equals(dict2[k])))
.ToList();
That produced a significantly faster run.
Here's how I produced my test data:
var dict1 = Enumerable.Range(0, 10000000).Select(x => Random.Shared.Next(2000000)).Distinct().ToDictionary(x => x.ToString(), x => (object)Random.Shared.Next(20));
var dict2 = Enumerable.Range(0, 10000000).Select(x => Random.Shared.Next(2000000)).Distinct().ToDictionary(x => x.ToString(), x => (object)Random.Shared.Next(20));
You could make it faster by avoiding to get separately the dict1[propName] and the dict2[propName]. You could get the value along with the key, either by enumerating directly the KeyValuePairs stored in the dictionary, or by calling the TryGetValue method:
foreach (var (key, value2) in dict2)
{
if (!dict1.TryGetValue(key, out var value1)
|| value1.ToString() != value2.ToString())
{
list.Add(key);
}
}

Build Dictionary with LINQ

Let's say we have a variable 'data' which is a list of Id's and Child Id's:
var data = new List<Data>
{
new()
{
Id = 1,
ChildIds = new List<int> {123, 234, 345}
},
new()
{
Id = 1,
ChildIds = new List<int> {123, 234, 345}
},
new()
{
Id = 2,
ChildIds = new List<int> {678, 789}
},
};
I would like to have a dictionary with ChildId's and the related Id's. If the ChildId is already in the dictionary, it should overwrite with the new Id.
Currently I have this code:
var dict = new Dictionary<int, int>();
foreach (var dataItem in data)
{
foreach (var child in dataItem.ChildIds)
{
dict[child] = dataItem.Id;
}
}
This works fine, but I don't like the fact that I am using two loops. I prefer to use Linq ToDictionary to build up the dictionary in a Functional way.
What is the best way to build up the dictionary by using Linq?
Why? I prefer functional code over mutating a state. Besides that, I was just curious how to build up the dictionary by using Linq ;-)
In this case your foreach appproach is both, readable and efficient. So even if i'm a fan of LINQ i would use that. The loop has the bonus that you can debug it easily or add logging if necessary(for example invalid id's).
However, if you want to use LINQ i would probably use SelectMany and ToLookup. The former is used to flatten child collections like this ChildIds and the latter is used to create a collection which is very similar to your dictionary. But one difference is that it allows duplicate keys, you get multiple values in that case:
ILookup<int, int> idLookup = data
.SelectMany(d => d.ChildIds.Select(c => (Id:d.Id, ChildId:c)))
.ToLookup(x => x.ChildId, x => x.Id);
Now you have already everything you needed since it can be used like a dictionary with same lookup performance. If you wanted to create that dictionary anyway, you can use:
Dictionary<int, int> dict = idLookup.ToDictionary(x => x.Key, x => x.First());
If you want to override duplicates with the new Id, as mentioned, simply use Last().
.NET-Fiddle: https://dotnetfiddle.net/mUBZPi
The SelectMany linq operator actually has a few less known overloads. One of these has a result collector which is a perfect use case for your scenario.
Following is an example code snippet to turn that into a dictionary. Note that I had to use the Distinct, since you had 2 id's with value 1 which had some duplicated child id's which would pose problems for a dictionary.
void Main()
{
// Get the data
var list = GetData();
// Turn it into a dictionary
var dict = list
.SelectMany(d => d.ChildIds, (data, childId) => new {data.Id, childId})
.Distinct()
.ToDictionary(x => x.childId, x => x.Id);
// show the content of the dictionary
dict.Keys
.ToList()
.ForEach(k => Console.WriteLine($"{k} {dict[k]}"));
}
public List<Data> GetData()
{
return
new List<Data>
{
new Data
{
Id = 1,
ChildIds = new List<int> {123, 234, 345}
},
new Data
{
Id = 1,
ChildIds = new List<int> {123, 234, 345}
},
new Data
{
Id = 2,
ChildIds = new List<int> {678, 789}
},
};
}
public class Data
{
public int Id { get; set; }
public List<int> ChildIds { get; set; }
}
The approach is to create pairs of each combination of Id and ChildId, and build a dictionary of these:
var list = new List<(int Id, int[] ChildIds)>()
{
(1, new []{10, 11}),
(2, new []{11, 12})
};
var result = list
.SelectMany(pair => pair.ChildIds.Select(childId => (childId, pair.Id)))
.ToDictionary(p => p.childId, p => p.Id);
ToDictionary will throw if there are duplicate keys, to avoid this you can look at this answer and create your own ToDictionary:
public static Dictionary<K, V> ToDictionaryOverWriting<TSource, K, V>(
this IEnumerable<TSource> source,
Func<TSource, K> keySelector,
Func<TSource, V> valueSelector)
{
Dictionary<K, V> output = new Dictionary<K, V>();
foreach (TSource item in source)
{
output[keySelector(item)] = valueSelector(item);
}
return output;
}
With LINQ you can achieve the result like this:
Dictionary<int, int> dict = (from item in data
from childId in item.ChildIds
select new { item.Id, childId}
).Distinct()
.ToDictionary(kv => kv.childId, kv => kv.Id);
Update:
Fully compatible version with foreach loop would use group by with Last(), instead of Distict():
Dictionary<int, int> dict2 = (from item in data
from childId in item.ChildIds
group new { item.Id, childId } by childId into g
select g.Last()
).ToDictionary(kv => kv.childId, kv => kv.Id);
As some already pointed out, depending on order of input elements does not feel "functional". LINQ expression becomes more convoluted then original foreach loop.
There is an overload of SelectMany which not only flattens the collection but also allows you to have any form of result.
var all = data.SelectMany(
data => data.ChildIds, //collectionSelector
(data, ChildId) => new { data.Id, ChildId } //resultSelector
);
Now if you want to transform all into a Dictionary, you have to remove the duplicate ChildIds first. You can use GroupBy as in below, and then pick the last item from each group (as you stated in your question you want to overwrite Ids as you go). The key of your dictionary should also be unique=ChildId:
var dict = all.GroupBy(x => x.ChildId)
.Select(x => x.Last())
.ToDictionary(x => x.ChildId, x => x.Id);
Or you can write a new class with IEquatable<> implemented and use it as the return type of resultSelector (instead of new { data.Id, ChildId }). Then write all.Reverse().Distinct().ToDictionary(x => x.ChildId); so it would detect duplicates based on your own implementation of Equals method. Reverse, because you said you want the last occurrence of the duplicates.

Replace Dictionary values C#

I have two dictionaries say
Dictionary<int,string> DictA=new Dictionary<int,string>();
DictA.Add(1,"A");
DictA.Add(2,"B");
DictA.Add(3,"C");
DictA.Add(4,"D");
Dictionary<string,string> DictB=new Dictionary<string,string>();
DictB.Add("A","A1");
DictB.Add("C","C1");
DictB.Add("D","D1");
Now I need a resultant dictionary as follows
<1,"A1">
<2,"B"> //Since B is missing in Second Dictionary it should
retain its old value
<3,"C1">
<4,"D1">
I am not happy with the iterator code I wrote for this using foreach, but I am certain that there is very short and cleaner way to do this using Lambda Expressions. I am very new to them and finding them difficult to understand.
Can someone explain a good resource to learn them and use efficiently with an example.
You can use LINQ.Basically just check if the second dictionary contains the value of first dictionary as Key, if it does get the value from second dictionary, if it doesn't get the value from the first dictionary:
DictA
.Select(x => new KeyValuePair<int,string>(x.Key,
DictB.ContainsKey(x.Value)
? DictB[x.Value]
: x.Value)
.ToDictionary(x => x.Key, x => x.Value);
you can use ToDictionary to generate the dictionary the way you want.
var result = DictA.ToDictionary(a => a.Key, a => DictB.ContainsKey(a.Value) ? DictB[a.Value] : a.Value);
foreach (var item in result)
{
Console.WriteLine(item.Key + ", " + item.Value);
}
and here is the result
1, A1
2, B
3, C1
4, D1
and if you want to do it inplace you can modify DictA
foreach (var key in DictA.Keys.ToList())
{
DictA[key] = DictB.ContainsKey(DictA[key]) ? DictB[DictA[key]] : DictA[key];
}
Very simple:
var result =
DictA
.ToDictionary(
a => a.Key,
a => DictB.ContainsKey(a.Value)
? DictB[a.Value]
: a.Value);
I get this result:
Since you want to do it inplace
DictA.ToList().ForEach(kv =>
{
if (DictB.ContainsKey(kv.Value))
DictA[kv.Key] = DictB[kv.Value];
});
Here's one way to do what you want:
public static Dictionary<TKey,TValue> Extend<TKey,TValue>( params Dictionary<TKey,TValue>[] sources )
{
return Extend<TKey,TValue>( null , sources ) ;
}
public static Dictionary<TKey,TValue> Extend<TKey,TValue>( IEqualityComparer<TKey> comparer , params Dictionary<TKey,TValue>[] sources )
{
var result = new Dictionary<TKey,TValue>( comparer ?? EqualityComparer<TKey>.Default ) ;
foreach( var src in sources )
{
if ( src == null ) throw new ArgumentNullException("sources", "source dictionary may not be null");
foreach( var item in src )
{
result[item.Key] = item.Value ;
}
}
return result ;
}
Here's another way, using Linq:
public static Dictionary<TKey,TValue> Extend<TKey,TValue>( params Dictionary<TKey,TValue>[] sources )
{
return Extend<TKey,TValue>( null , sources ) ;
}
public static Dictionary<TKey,TValue> Extend<TKey,TValue>( IEqualityComparer<TKey> comparer , params Dictionary<TKey,TValue>[] sources )
{
return sources
.SelectMany( kvp => kvp )
.GroupBy( kvp => kvp.Key , kvp => kvp.Value , comparer )
.ToDictionary( grp => grp.Key , grp => grp.Last() , comparer )
;
}
But I'm willing to bet that the first, straightforward method will be faster.

Extracting a Dictionary<TKey, double> from a Dictionary<TKey, string> instance

I have a generic Dictionary where TValue is of type String (Dictionary<int, string>). I chose to use string as the value type because the data was loaded from an Xml file where the source values can be character or numeric data types (I suppose Object would've been an acceptable TValue type too, but even then this question would be equally applicable).
The character data types also have importance, so excluding them outright isn't an option.
I'd like to extract a subset of this Dictionary<int, double>. In other words, I'd like the subset of the dictionary where the values are numeric.
Right now I'm doing it like this:
Dictionary<int, string> myDictionary;
// Do some loading.
var numericData = myDictionary.Where(kvp => Double.TryParse(kvp.Value, out temp)
This approach is awfully ugly and doesn't get me the result as a Dictionary<int, double> Can anyone offer other ways to improve this?
Thanks!
The code you've given is not only ugly - it will fail with an InvalidCastException at execution time. I suspect you actually want:
var numericData = myDictionary
.Select(kvp => {
double value;
return new { kvp.Key,
Value = double.TryParse(kvp.Value, out value)
? value : (double?) null
};
})
.Where(pair => pair.Value != null)
.ToDictionary(pair => pair.Key, pair => pair.Value.Value);
And yes, that's ugly - but:
It avoids parsing the value more than once
It avoids putting side-effects in your query
You can make it slightly cleaner but less efficient if you're happy to parse twice:
var numericData = myDictionary
.Where(kvp => { double tmp; return double.TryParse(kvp.Value, out tmp); })
.ToDictionary(pair => pair.Key, pair => double.Parse(pair.Value));
Or (more cleanly) you could create a separate method:
public static double? TryParseNullableDouble(string text)
{
double value;
return double.TryParse(text, out value) ? value : (double?) null;
}
Then the first version becomes:
var numericData = myDictionary
.Select(kvp => new { kvp.Key, TryParseNullableDouble(kvp.Value) })
.Where(pair => pair.Value != null)
.ToDictionary(pair => pair.Key, pair => pair.Value.Value);
You can just create a new dictionary using the temp variable which contains the double value - this exploits the fact that the enumeration and addition to the dictionary is done item by item so temp contains the correct double value:
double temp = 0;
var numDict = myDictionary.Where(kvp => Double.TryParse(kvp.Value, out temp))
.ToDictionary( x=> x.Key, x=> temp);

Convert List<MyObject> to Dictionary <obj.string, List<obj.ID>>

I would like to take a list of objects and convert it to a dictionary where the key is a field in the object, and the value is a list of a different field in the objects that match on the key. I can do this now with a loop but I feel this should be able to be accomplished with linq and not having to write the loop. I was thinking a combination of GroupBy and ToDictionary but have been unsuccessful so far.
Here's how I'm doing it right now:
var samplesWithSpecificResult = new Dictionary<string, List<int>>();
foreach(var sample in sampleList)
{
List<int> sampleIDs = null;
if (samplesWithSpecificResult.TryGetValue(sample.ResultString, out sampleIDs))
{
sampleIDs.Add(sample.ID);
continue;
}
sampleIDs = new List<int>();
sampleIDs.Add(sample.ID);
samplesWithSpecificResult.Add(sample.ResultString, sampleIDs);
}
The farthest I can get with .GroupBy().ToDictionay() is Dictionary<sample.ResultString, List<sample>>.
Any help would be appreciated.
Try the following
var dictionary = sampleList
.GroupBy(x => x.ResultString, x => x.ID)
.ToDictionary(x => x.Key, x => x.ToList());
The GroupBy clause will group every Sample instance in the list by its ResultString member, but it will keep only the Id part of each sample. This means every element will be an IGrouping<string, int>.
The ToDictionary portion uses the Key of the IGrouping<string, int> as the dictionary Key. IGrouping<string, int> implements IEnumerable<int> and hence we can convert that collection of samples' Id to a List<int> with a call to ToList, which becomes the Value of the dictionary for that given Key.
Yeah, super simple. The key is that when you do a GroupBy on IEnumerable<T>, each "group" is an object that implements IEnumerable<T> as well (that's why I can say g.Select below, and I'm projecting the elements of the original sequence with a common key):
var dictionary =
sampleList.GroupBy(x => x.ResultString)
.ToDictionary(
g => g.Key,
g => g.Select(x => x.ID).ToList()
);
See, the result of sampleList.GroupBy(x => x.ResultString) is an IEnumerable<IGrouping<string, Sample>> and IGrouping<T, U> implements IEnumerable<U> so that every group is a sequence of Sample with the common key!
Dictionary<string, List<int>> resultDictionary =
(
from sample in sampleList
group sample.ID by sample.ResultString
).ToDictionary(g => g.Key, g => g.ToList());
You might want to consider using a Lookup instead of the Dictionary of Lists
ILookup<string, int> idLookup = sampleList.ToLookup(
sample => sample.ResultString,
sample => sample.ID
);
used thusly
foreach(IGrouping<string, int> group in idLookup)
{
string resultString = group.Key;
List<int> ids = group.ToList();
//do something with them.
}
//and
List<int> ids = idLookup[resultString].ToList();
var samplesWithSpecificResult =
sampleList.GroupBy(s => s.ResultString)
.ToDictionary(g => g.Key, g => g.Select(s => s.ID).ToList());
What we 're doing here is group the samples based on their ResultString -- this puts them into an IGrouping<string, Sample>. Then we project the collection of IGroupings to a dictionary, using the Key of each as the dictionary key and enumerating over each grouping (IGrouping<string, Sample> is also an IEnumerable<Sample>) to select the ID of each sample to make a list for the dictionary value.

Categories