Using LINQ to build a Dictionary from a List of delimited strings - c#

I have a list of strings that look like this:
abc|key1|486997
def|key1|488979
ghi|key2|998788
gkl|key2|998778
olz|key1|045669
How can I use LINQ and ToDictionary to produce a Dictionary<string, List<string>> that looks like
key1 : { abc|key1|486997, def|key1|488979, olz|key1|045669 }
key2 : { ghi|key2|998788, gkl|key2|998778 }
Basically I want to be able to extract the second element as the key use ToDictionary() to create the dictionary in one go-round.
I'm currently doing this ..
var d = new Dictionary<string, List<string>>();
foreach(var l in values)
{
var b = l.Split('|');
var k = b.ElementAtOrDefault(1);
if (!d.ContainsKey(k))
d.Add(k, new List<string>());
d[k].Add(l);
}
I've seen the questions on building dictionaries from a single string of delimited values, but I'm
wondering if there's an elegant way to do this when starting with a list of delimited strings instead.

var list = new []
{
"abc|key1|486997",
"def|key1|488979",
"ghi|key2|998788",
"gkl|key2|998778",
"olz|key1|045669"
};
var dict = list.GroupBy(x => x.Split('|')[1])
.ToDictionary(x => x.Key, x => x.ToList());
You can also transform it to a lookup (that is very similary to a Dictionary<K,IEnumerable<V>>) in one shot:
var lookup = list.ToLookup(x => x.Split('|')[1]);

var data = new[]
{
"abc|key1|486997",
"def|key1|488979",
"ghi|key2|998788",
"gkl|key2|998778",
"olz|key1|045669"
};
var dictionary = data.Select(row => row.Split('|'))
.GroupBy(row => row[1])
.ToDictionary(group => group.Key, group => group);

If your data is guaranteed to be consistent like that, you could do something like this:
var data = new[]
{
"abc|key1|486997",
"def|key1|488979",
"ghi|key2|998788",
"gkl|key2|998778",
"olz|key1|045669"
};
var items = data
.GroupBy(k => k.Split('|')[1])
.ToDictionary(k => k.Key, v => v.ToList());

Related

Improving conversion from List to List<Dictionary<string,string>> with Linq

I've a Key/Value table in my DB and I would return a List of Dictionary.
The following code works fine for me but with a lot of data is not performing.
note: r.name doesn't contains unique value
List<Dictionary<string, string>> listOutput = null;
using (ExampleDB db = new ExampleDB())
{
var result = (from r in db.FormField
where r.Form_id == 1
select new { r.ResponseId, r.name, r.value}).toList();
listOutput = new List<Dictionary<string, string>>();
foreach (var element in result)
{
listOutput.Add((from x in listOutput
where x.ResponseId == element.ResponseId
select x).ToDictionary(x => x.name, x => x.value));
}
}
return listOutput;
Do you have suggestions on how to improve this code ?
I suspect you want something like:
List<Dictionary<string, string>> result;
using (var db = new ExampleDB())
{
result = db.FormField
.Where(r => r.Form_id == 1)
.GroupBy(r => r.ResponseId, r => new { r.name, r.value })
.AsEnumerable()
.Select(g => g.ToDictionary(p => p.name, p => p.value))
.ToList();
}
In other words, we're filtering so that r.Form_id == 1, then grouping by ResponseId... taking all the name/value pairs associated with each ID and creating a dictionary from those name/value pairs.
Note that you're losing the ResponseId in the list of dictionaries - you can't tell which dictionary corresponds to which response ID.
The AsEnumerable part is to make sure that the last Select is performed using LINQ to Objects, rather than trying to convert it into SQL. It's possible that it would work without the AsEnumerable, but it will depend on your provider at the very least.
From what I gather you're trying to create a list of Key/Value pairs based on each ResponseId. Try GroupBy:
var output = result.GroupBy(r => r.ResponseId)
.Select(r => r.ToDictionary(s => s.Name, s => s.Value));
This will return an IEnumerable<Dictionary<string,string>>, which you can ToList if you actually need a list.

Get a filtered Dictionary from a Dictionary using LINQ

I have a dictionary of type (string, string) which is a list of groups and their IDs. I need the 2nd dictionary which should not have five groups (which I know). Can anyone help me in framing the select in LinQ which I can use to create this sub dictionary?
var myDic = new Dictionary<string,string>();
myDic.Add("1","One");
myDic.Add("2","Two");
myDic.Add("3","Three");
myDic.Add("4","Four");
//myDic.Dump();
var exclusions = new []{"2","3"};
var newDict = myDic.Where(x=> !exclusions.Contains(x.Key))
.ToDictionary(x=> x.Key, x=> x.Value);
//newDict.Dump();
var groups = new List<string>();
// Fill your groups
var yourDict = new Dictionary<string, string>();
//fill your dict.
var filteredDict = yourDict.Where(a => !groups.Contains(a.Key)).ToDictionary(k=>k.Key,v=>v.Value);
I think this should to the trick:
Dictionary<string, string> myDic;
...
var badGroups = new[] { "badGroup1", "badGroup2", ... };
var my2ndDic = myDic
.Where(e => !badGroups.Contains(e.Key))
.ToDictionary(e => e.Key, e.Value);
I suppose what you missed was the .ToDictionary() method.

Linq query to with SortedList<int,list<int>>

I am currently using Linq to retrieve a list of distinct values from my data table. I am then looping through the list and again calling a linq query to retrieve a list of values for each
value in the first list.
_keyList = new SortedList<int, List<int>>();
var AUGroupList = ProcessSummaryData.AsEnumerable()
.Select(x => x.Field<int>("AUGroupID"))
.Distinct()
.ToList<int>();
foreach (var au in AUGroupList)
{
var AUList = ProcessSummaryData.AsEnumerable()
.Where(x => x.Field<int>("AUGroupID") == au)
.Select(x => x.Field<int>("ActivityUnitID"))
.ToList<int>();
_keyList.Add(au, AUList);
}
I am then adding the value to a sorted list along with the corresponding second list.
How can I combine the above two queries into one Linq query so that I don't have to call them separately?
You should be able to do something like:
var groupQuery = from d in ProcessSummary.AsEnumerable()
group d by new { Key = d.Field<int>("AUGroupID") } into g
select new { GroupID = g.Key, Values = g.Distinct().ToList() };
Then you can loop through the groupQuery and populate the sorted list. The Key property will contain the group id, and the Values property will have a distinct list of values.
Have you tried this?
var _keyList = new SortedList<int, List<int>>();
var AUGroupList = ProcessSummaryData.AsEnumerable()
.Select(x => x.Field<int>("AUGroupID"))
.Distinct()
.Where(x => x.Field<int>("AUGroupID") == au)
.Select(x => x.Field<int>("ActivityUnitID"))
.ToList<int>();
_keyList.Add(au, AUList);
}
Your provider should cope with that, if not there's a few other ways.

LINQ casting during enumeration

I have a List<string>
List<string> students;
students.Add("Rob");
students.Add("Schulz");
and a Dictionary<string,string>
Dictionary<string, string> classes= new Dictionary<string, string>();
classes.Add("Rob", "Chemistry");
classes.Add("Bob", "Math");
classes.Add("Holly", "Physics");
classes.Add("Schulz", "Botany");
My objective now is to get a List with the values - Chemistry and Botany - for which I am using this
var filteredList = students.Where(k => classes.ContainsKey(k))
.Select(k => new { tag = students[k] });
While trying to enumerate the values - I am able to obtain - tag=Chemistry & tag=Botany...while I want just Chemistry and Botany.
What is the appropriate casting to be applied? Is there a better way to get to these values?
You only have to write:
var filteredList = students.Where(student => classes.ContainsKey(student));
Here, student is a string, since students is a List<string>, so you only have to apply Where(). The result will be an IEnumerable<string>.
You can apply ToList() if you want to exhaust the enumerable into another List<string>:
var filteredList = students.Where(student => classes.ContainsKey(student)).ToList();
If you want a list of classes (it's not clear from the code in your question), then you have to apply Select() to project classes from students:
var filteredList = students.Where(student => classes.ContainsKey(student))
.Select(student => classes[student]);
try:
var filteredList = students.Where(k => classes.ContainsKey(k))
.Select(k => students[k]);
var filteredList = students.Where(k => classes.ContainsKey(k))
.Select(k => students[k]);

what is the best way to convert one dictionary to another

i have one dictionary that has entries that looks like this:
dictionary["ABC.123"] = "Test"
dictionary["DEF.123"] = "Test2"
dictionary["EFG.123"] = "Test3"
dictionary["EFG.343"] = "Test3"
dictionary["ABC.456"] = "Test"
and i want to create a new dictionary that looks like this: (basically parse out the beginning string before the "." and create that as the key and use the same value.
dictionary["ABC"] = "Test"
dictionary["DEF"] = "Test2"
dictionary["EFG"] = "Test3"
as you can see: there is always a 1 to 1 mapping between the first part of the key in the first dictionary to the value so there will be no clashes
what is the easiest way using LINQ to convert to create the second dictionary from the first ??
I can loop through each record manually and parse out the new key and create each new record but i figured there was a LINQ way of doing it
var newDict = dictionary.GroupBy(kvp => kvp.Key.Remove(kvp.Key.IndexOf('.')))
.ToDictionary(grp => grp.Key, grp => grp.First().Value);
Although a plain foreach is probably going to be more readable and efficient:
var newDict = new Dictionary<string, string>();
foreach (var kvp in dictionary)
{
newDict[kvp.Key.Remove(kvp.Key.IndexOf('.'))] = kvp.Value;
}
A simple approach is to use the Select method to generate your desired KeyValuePair in conjuntion with ToDictionary
var response = dictionary
.Select(kvp => new KeyValuePair<string, string>(kvp.Key.Remove(kvp.Key.IndexOf('.')), kvp.Value))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
var dict2 = dictionary.GroupBy(x => x.Key.Split('.')[0])
.ToDictionary(g => g.Key, g => g.First().Value);

Categories