very new to c#.
I'm working with an API that returns a dictionary with 2 keys and 1 value.
This is my current code:
var dic = API.getVehicleValidMods((VehicleHash)sender.vehicle.model);
foreach (KeyValuePair<int, Dictionary<int, string>> kvp in dic)
{
Dictionary<int, string> kvp2 = kvp.Value;
foreach (KeyValuePair<int, string> kvp3 in kvp2)
{
API.consoleOutput("Key = {0}, Key = {1}, Value = {2}", kvp.Key, kvp3.Key, kvp3.Value);
Here is a sample from the result it returns:
As you can see the first keys are sometimes the same number and they sometimes skip numbers.
I'm trying to return all matching "key number 2" that matches a certain "key 1".
The string values are not of importance in my case, I'm only interested in the ints.
So, my pseudo logic tells me something like
"foreach keynumber2 in keynumber1 (the integer, eg. 23) do this:"
should work, however I'm unsure how to code it properly.
tl;dr How do i find all "key2" that matches a certain "key1" ?
So, you want to find all inner keys of specific outer key. You can do this with this line of code:
var key1 = 42;
var allKeys = dic.ContainsKey(key1)
? dic[key1].Keys.ToArray()
: new int[0];
If outer dictionary contains 42 as a key this will return all inner keys. Otherwise it will return an empty ints array.
var matchesFrom1 = from val in dic where val.Key == 1 select val.Value;
var valueList = from val in matchesFrom1 where val.Key == 2 select val.Value;
valueList is the string of values returned
You could use LINQ methods:
Use .Where method to filter your initial collection of KeyValuePairs<> by key.
Use SelectMany to flatten resulting IEnumerable<KeyValuePair<int, Dictionary<>>> to a simple IEnumerable<int>
Use Distinct to make sure there are no duplicate keys (or omit this part if it is not necessary).
var someKey = 16;
var result = dic
.Where(x => x.Key == someKey)
.SelectMany(kv => kv.Value.Keys)
.Distinct()
.ToArray();
Related
I am trying to create a dictionary from a list of strings in which each string contains delimiter separated values. There are many values that are being repeated so I need one key with multiple values
I Have already tried creating Dictionary<string, List<string>> but this does not work
Each string is like
values = Man: rev
sum:2
Man: rev2
sum:2
values is a list of strings
var d = new Dictionary<string, List<string>>();
foreach(var l in values)
{
var b = l.Split(':');
var k = b.ElementAtOrDefault(0);
if (!d.ContainsKey(k))
d.Add(k, new List<string>());
d[k].Add(l);
}
When I try to search the dictionary using the method I mentioned above I am not able to retrieve the value for a particular key
Based on the comments provided I would suggest using the following construct (I can't test the code right now, but can fix it if needed):
Dictionary<string,string> result = values
.Select(v => v.Split(new char[] {':'}, StringSplitOptions.RemoveEmptyEntries)
.ToLookup(arr => arr.FirstOrDefault(), arr => String.Concat(arr.Skip(1))
.ToDictionary(g => g.Key, g => String.Join(", ", g.Select(i => i.Trim());
Explanation: first Select does the splitting; ToLookup creates the > structure using the first element as the key and the rest of the value string as the item (in the original question there was the whole string added - I'm not sure if that was the intention); ToDictionary then converts the List into a string (based on the comments as the desired output seems to be Distionary)
I have a dictionary which has an integer Key that represents a year, and a Value which is a list of object Channel. I need to flatten the data and create a new object from it.
Currently, my code looks like this:
Dictionary<int, List<Channel>> myDictionary;
foreach(var x in myDictionary)
{
var result = (from a in x.Value
from b in anotherList
where a.ChannelId == b.ChannelId
select new NewObject
{
NewObjectYear = x.Key,
NewObjectName = a.First().ChannelName,
}).ToList();
list.AddRange(result);
}
Notice that I am using the Key to be the value of property NewObjectYear.
I want to get rid of foreach since the dictionary contains a lot of data and doing some joins inside the iteration makes it very slow. So I decided to refactor and came up with this:
var flatten = myDictionary.SelectMany(x => x.Value.Select(y =>
new KeyValuePair<int, Channel>(x.Key, y))).ToList();
But with this, I couldn't get the Key directly. Using something like flatten.Select(x => x.Key) is definitely not the correct way. So I tried finding other ways to flatten that would be favorable for my scenario but failed. I also thought about creating a class which will contain the year and the list from the flattened but I don't know how.
Please help me with this.
Also, is there also another way that doesn't have the need to create a new class?
It seems to me you are trying to do only filtering, you do not need join for that:
var anotherListIDs = new HashSet<int>(anotherList.Select(c => c.ChannelId));
foreach (var x in myDictionary)
{
list.AddRange(x.Value
.Where(c => anotherListIDs.Contains(c.ChannelId))
.Select(c => new NewObject
{
NewObjectYear = x.Key,
NewObjectName = c.First().ChannelName,
}));
}
You do realise, that if the second element of the list in a specific dictionary element has a matching channelId, that you return the first element of this list, don't you?
var otherList = new OtherItem[]
{
new OtherItem() {ChannelId = 1, ...}
}
var dictionary = new Dictionary<int, List<Channel>[]
{
{ 10, // Key
new List<Channel>() // Value
{
new Channel() {ChannelId = 100, Name = "100"},
new Channel() {ChannelId = 1, Name = "1"},
},
};
Although the 2nd element has a matching ChannelId, you return the Name of the first element.
Anyway, let's assume this is what you really want. You are right, your function isn't very efficient.
Your dictionary implements IEnumerable<KeyValuePair<int, List<Channel>>. Therefore every x in your foreach is a KeyValuePair<int, List<Channel>. Every x.Value is a List<Channel>.
So for every element in your dictionary (which is a KeyValuePair<int, List<Channel>), you take the complete list, and perform a full inner join of the complete list with otherList, and for the result you take the key of the KeyValuePair and the first element of the List in the KeyValuePair.
And even though you might not use the complete result, but only the first or the first few, because of FirstOrDefault(), or Take(3), you do this for every element of every list in your Dictionary.
Indeed your query could be much more efficient.
As you use the ChannelIds in your OtherList only to find out if it is present, one of the major improvements would be to convert the ChannelIds of OtherList to a HashSet<int> where you have superior fast lookup to check if the ChannelId of one of the values in your Dictionary is in the HashSet.
So for every element in your dictionary, you only have to check every ChannelId in the list to see if one of them is in the HashSet. As soon as you've found one, you can stop and return only the first element of the List and the Key.
My solution is an extension function of Dictionary>. See Extension Methods Demystified
public static IEnumerable<NewObject> ExtractNewObjects(this Dictionary<int, List<Channel>> dictionary,
IEnumerable<OtherItem> otherList)
{
// I'll only use the ChannelIds of the otherList, so extract them
IEnumerable<int> otherChannelIds = otherList
.Select(otherItem => otherItem.ChannelId);
return dictionary.ExtractNewObjects(otherChannelIds);
}
This calls the other ExtractNewobjects:
public static IEnumerable<NewObject> ExtractNewObjects(this Dictionary<int, List<Channel>> dictionary,
IEnumerable<int> otherChannelIds)
{
var channelIdsSet = new HashSet<int>(otherChannelIds));
// duplicate channelIds will be removed automatically
foreach (KeyValuePair<int, List<Channel>> keyValuePair in dictionary)
{
// is any ChannelId in the list also in otherChannelIdsSet?
// every keyValuePair.Value is a List<Channel>
// every Channel has a ChannelId
// channelId found if any of these ChannelIds in in the HashSet
bool channelIdFound = keyValuePair.Value
.Any(channel => otherChannelIdsSet.Contains(channel.ChannelId);
if (channelIdFound)
{
yield return new NewObject()
{
NewObjectYear = keyValuePair.Key,
NewObjectName = keyValuePair.Value
.Select(channel => channel.ChannelName)
.FirstOrDefault(),
};
}
}
}
usage:
IEnumerable<OtherItem> otherList = ...
Dictionary<int, List<Channel>> dictionary = ...
IEnumerable<Newobject> extractedNewObjects = dictionary.ExtractNewObjects(otherList);
var someNewObjects = extractedNewObjects
.Take(5) // here we see the benefit from the yield return
.ToList();
We can see four efficiency improvements:
the use of HashSet<int> enables a very fast lookup to see if the ChannelId is in OtherList
the use of Any() stops enumerating the List<Channel> as soon as we've found a matching Channelid in the HashSet
the use of yield return makes that you don't enumerate over more elements in your Dictionary than you'll actually use.
The use of Select and FirstOrDefault when creating NewObjectName prevents exceptions if List<Channel> is empty
I have a Dictionary<int?,List<string>>. I need to output pairs of keys and values sorted by keys in ascending order. First, I think to use OrderedDictionary but it saves both keys and values in type of object. Maybe somehow it could be done through the extension methods?
You have two options here:
Use a SortedList<TKey,TValue>/SortedDictionary<TKey,TValue>:
var sortedData = new SortedList<int?, List<string>>(currentDictionary);
Use Linq's OrderBy:
var sortedData = currentDictionary.OrderBy(x => x.Key);
You can use any of these options with the following printing:
foreach (var entry in sortedData)
{
Console.WriteLine("Key: {0}, Values: ", entry.Key);
foreach (var value in entry.Value)
{
Console.WriteLine(value);
}
}
Dictionary<int?, List<string>> yourDictionary = GetTheDictionary();
var sortedKeys = yourDictionary.Select(kvp => kvp.Key).OrderBy(k => k);
This will give you a list of all of your keys in ascending order
If you want your dictionary the same. Ie still as Key Value Pairs. Just ordered by the key then you need to do.
yourDictionary.OrderBy(kvp => kvp.Key);
I have some dictionary. Let it be:
var dictionary = new Dictionary< string, List<MyClass>>();
I need to convert it to list of object that contains property from key and value.
I did it with foreach loop in the next way:
var list = new List<dynamic>();
foreach (var key in dictionary.Keys)
{
var values = dictionary[key];
foreach (var obj in values)
{
list.Add(new
{
obj.Property0,
obj.Property1,
obj.Property2,
...
key
}
);
}
}
It works, but looks rudely as for me. Is it possible to do it more gracefully with LINQ?
You can do that with a SelectMany.
var list = dictionary.SelectMany(
kvp => kvp.Value,
(kvp,obj) => new {obj.Property0, obj.Property1, obj.Property2, kvp.Key})
.ToList();
Or in query snytax
var list = (from kvp in dictionary
from obj in kvp.Value
select new {obj.Property0, obj.Property1, obj.Property2, kvp.Key})
.ToList();
Note this results in a list of an anonymous class. If you really want dynamic you'll need to do a Cast<dynamic>() before the ToList(). Also if you want the last property of the anonymous class to be key instead of Key you'll need to do key = kvp.Key.
I have a dictionary that has int keys. The keys are in random order and are not necesserily consequtive (e.g. 5, 3, 11, 12, 10, 4). I would like to visit each key-value pair in reverse order of key size. So for the example above I'd like to visit (12,11,10...).
The way I see how to do this is to get a count of the number of elements, find the max key by say binary search and then find the next largest value that is smaller than the current max etc. untill I've processed the number of elements contained in the dictionary.
However, there could be a method already existing. For a discussion on how to find the max key: Get the largest key in a dictionary
var pairs = dictionary.OrderByDescending(pair => pair.Key);
foreach(var pair in pairs)
{
var value = pair.Value;
...
}
foreach (var p in myDict.OrderByDescending(pair => pair.Key)) {
// process pair
}
Well, it's easy enough to retrieve all of the keys from a dictionary, you can then use the LINQ OrderByDescending() operator to get them in reverse order:
foreach( var key in yourDictionary.Keys.OrderByDescending(x => x) )
{
// your logic here
}
If you need the value associated with the key, you can also do:
foreach( var keyValuePair in yourDictionary.OrderByDescending(kvp => kvp.Key) )
{
// your logic here
}
You can, of course, use query comprehension syntax is LINQ as well:
var yourResult = from kvp in dictionary
order by kvp.Key descending
select YourProjectionFunction(kvp);
dic = dic.OrderByDescending(p=>p.Key).ToDictionary(p => p.Key, p => p.Value);