LinQ ofType in Value - c#

I have the following Dictionary:
public Dictionary<string,object> Items;
Now I need to get all Items where the Value of the Dictionary-item is from a specific type. (e.g. "int")
var intValues = Items.OfType<KeyValuePair<string,int>> simply does not work.
Code without LinQ would be something like:
var intValues=new Dictionary<string,int>()
foreach (var oldVal in Items) {
if (oldVal.Value is int) {
intValues.add(oldVal.Key, oldVal.Value);
}
}
(Update) my example should show the basic idea. But if possible I would avoid to create a new Dictionary as a result.

The direct translation of your foreach would be the following in LINQ:
var intValues = Items.Where(item => item.Value is int)
.ToDictionary(item => item.Key, item => (int)item.Value);
So basically, you filter first for where the item.Value is an int, and then you create a dictionary from it using ToDictionary where you cast those values to int to make sure that the resulting dictionary is a Dictionary<string, int>. Since we filtered non-integers already, this type cast will always succeed.

You can use the is operator on the Value property:
var intValues = Items.Where(x => x.Value is int);
If you want an actual Dictionary<string,int> at the end just add:
.ToDictionary(v=> v.Key, v=> (int)v.Value)

Try with this:
var intValue = Items
.Where(x => x.Value is int) // filter per Value is int
.ToDictionary(x => x.Key, x => (int)x.Value); // create a new dictionary converting Value to int

You can do
var result = Items.Where(x => x.Value is int)
.ToDictionary(x => x.Key, x => x.Value);

Related

Calculate average in Dictionary <string, List<double>> C#

I need calculate the average of a List into a dictionary, I´m start using this syntax to access to the double List and gets the average but the compiler give an error, I´m new working with dictionarys, how is the correct syntax to access each double List and gets the average?
Here is the example of my code:
Dictionary<string, List<double>> SignalValues =
new Dictionary<string, List<double>>();
var dSignalAvg = SignalValues
.Where(x => x.Key == "Key")
.Average(x => x.Value);
Thank you so much!
If you are sure that the specific key exists in the dictionary then you can simply do:
var dSignalAvg = SignalValues["Key"].Average();
The code above will throw an exception if the dictionary doesn't have a key that equals "Key". If you want to check first, you can use TryGetValue like this:
double dSignalAvg = 0;
if (SignalValues.TryGetValue("Key", out List<double> signalList))
{
dSignalAvg = signalList.Average();
}
Like this?
Given a dictionary like this:
Dictionary<string,List<double>> values = new Dictionary<string,List<double>>();
Getting the average for a specific key is easy:
double avgValue = values
.Where( x => x.Key == "Key")
.Select( x => x.Value.Average() )
.FirstOrDefault()
;
Or, if you want to reduce your dictionary to a dictionary of averages, it's just:
Dictionary<string,double> avgValues = values
.ToDictionary(
x => x.Key ,
x => x.Value.Average()
)
;

Convert ForEach Dictionary Value If Condition And Add Key to List

May I know how can I convert the following statement to LINQ?
Dictionary<Guid, Guid> BatchID_To_RunID = new Dictionary<Guid, Guid>();
List<Guid> BatchIDList_InCase = new List<Guid>();
foreach (var x in BatchID_To_RunID)
{
if (x.Value == RunID)
{
Guid BatchID = x.Key;
BatchIDList_InCase.Add(x.Key);
}
}
Thank you
Well, the if is effectively a filter, suggesting a Where call, and the use of x.Key is effectively a projection, suggesting a Select call. So assuming BatchIDList_InCase is empty before this loop, you could replace it with:
// TODO: Rename BatchID_To_RunID to follow .NET naming conventions
var batchIds = BatchID_To_RunID
.Where(pair => pair.Value == RunID)
.Select(pair => pair.Key)
.ToList();
Or using a query expression:
var batchIds = (from pair in BatchID_To_RunID where pair.Value == RunID select pair.Key)
.ToList();
So you have a dictionary BatchID_To_RunID and a variable RunId
Requirement
Give me the keys of all items in the dictionary that have a Value equal to RunId.
If you write it precisely like this, the solution is not very difficult:
GUID RunId = ...
var result = BatchID_To_RunID // this is a sequence of KeyValuePair<GUID, GUID>
// Keep only those KeyValuePairs with a value equal to RunId
.Where(keyValuePair => keyValuePair.Value == RunId)
// from the remaining keyValuePairs, Select the Key
.Select(keyValuepair => keyValuePair.Key)
// and convert this sequence of GUIDs into a List:
.ToList();
Simple comme bonjour!
This is one way to do it:
BatchIDList_InCase.AddRange(BatchID_To_RunID.Where(b => b.Value == RunID).Select(b => b.Key).ToList());

C# Select only few member pairs from Object array and store it in Dictionary [duplicate]

This question already has answers here:
How to convert array to dictionary
(2 answers)
Closed 5 years ago.
I have an Object array of my class (Item) like below,
class Item
{
int id;
int value;
bool isSelected;
}
Item itemArr[] = new Item [MAXCOUNT];
To retrieve the selected Item ids and store into list, I am using below code
List<int> listOfItems = new List<int>();
listOfItems.AddRange(itemArr.Where(p => (p.IsSelected)
.Select(p => p.ID)).ToArray());
I have one more Dictionary which indicates item id as key and item value as value,
Dictionary<int,int> itemidValueMap = new Dictionary<int,int>();
How to get item id and value pair and store it into this dictionary?
This question is not duplicate. I need to select only the elements which satisfies the condition and store only them in dictionary. But previous question says about adding all the elements into dictionary.
To get the selected IDs your query should look like this:
List<int> selectedIDs = itemArr.Where(item => item.IsSelected).Select(item => item.id).ToList();
Note that the result is a List<int>, not a List<Item>.
To get your dictionary try this:
var idToValue = itemArr.ToDictionary(item => item.id, item => item.value);
This only works if your IDs are unique. If they are not, you'll need to group them, but I don't know how you want to aggregate the values of items with the same ID.
If you want that dictionary only for selected items, you simply insert the Where clause again:
var idToValue = itemArr.Where(item => item.IsSelected).ToDictionary(item => item.id, item => item.value);
To construct the dictionary use ToDictionary:
var dictionary = itemArr.Where(p => p.IsSelected)
.ToDictionary(key => key.id, value => value.value);
If you have the same id a few times it will cause an exception (keys are not unique) so use ToLookup or first GroupBy and then ToDictionary.
var lookup = itemArr.Where(p => p.IsSelected)
.ToLookup(key => key.id, value => value.value);
var dictionary = itemArr.Where(p => p.IsSelected)
.GroupBy(i => i.id)
.ToDictionary(key => key.Key, value => value.Select(i => i.value));
As a side note you can populate the ID list a bit nicer:
var listOfItems = itemArr.Where(p => p.IsSelected).Select(p => p.ID).ToList();
You need to change this
listOfItems.AddRange(itemArr.Where
(p => (p.IsSelected).Select(p => p.ID)).ToArray());
to
listOfItems.AddRange(itemArr.Where
(p => p.IsSelected).ToArray());
then
Dictionary<int,int> itemidValueMap = listOfItems.ToDictionary(item => item.id,
item => item.value)

C# How can I Cast/Convert a IEnumerable<IGrouping<object, object>> to another type?

So, I am trying to cast the following variable:
IEnumerable<IGrouping<object, object>> GroupedValues;
To this:
IEnumerable<IGrouping<int, int>> GroupedValues;
I have tried using Cast and also
Convert.ChangeType(GroupedValues, typeof(int));
but if It doesn't seem like a valid conversion if I compare it with a IEnumerable<IGrouping<int, int>> variable;
You can do this
var result = GroupedValues.GroupBy(x => (int)x.Key, x => x.Cast<int>());
This will throw exception if any object is not int. so you can filter to get only integer items like this.
var result = GroupedValues.Where(x => x.Key is int && x.All(i => i is int)).GroupBy(x => (int)x.Key, x => x.Cast<int>());
You can't cast it because it's not contravariant- you could reproject it (assuming that all keys and values are castable to int):
var q = GroupedValues.SelectMany(g => g.Select (gv => new {Key = (int)g.Key, Item = (int)gv}))
.GroupBy (g => g.Key, g => g.Item);
Or change the initial query to case the key and items.

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