Nested foreach to LINQ in multi-level dictionary - c#

I would like to simplify below nested foreach loops using LINQ but couldn't figure out the way. I guess I can use SelectMany using lambda but not sure. I want to create list of objects of ClassA after this nested iteration. Any help is appreciated:
public List<ClassA> GetLists(Dictionary<string, Dictionary<IEnumerable, Dictionary<string, ClassB>>> groups)
{
var retOutput = new List<ClassA>();
foreach (KeyValuePair<string, Dictionary<IEnumerable, Dictionary<string, ClassB>>> group1 in groups)
{
foreach (KeyValuePair<IEnumerable, Dictionary<string, ClassB>> group2 in group1.Value)
{
foreach (KeyValuePair<string, ClassB> group3 in group2.Value)
{
GetList(retOutput, group1.Key,
group2.Key,
group3);
}
}
}
return retOutput;
}
private static void GetList(List<ClassA> retOutput,
string group1Key,
IEnumerable group2Key,
KeyValuePair<string, ClassB> group3)
{
List<List<string>> itemIdsLists = group3.Value.ItemId.IntoChunks(2000);
foreach (var itemIdList in itemIdsLists)
{
var currentRequest = new ClassA
{
TransactionType = group1Key,
Filters = new Dictionary<string, object>(),
ItemIds = new List<string>(),
PropStreamsDict = new Dictionary<string, Tuple<long, string>>()
};
if (group2Key is Dictionary<string, object>)
{
currentRequest.Filters = (Dictionary<string, object>)group2Key;
}
currentRequest.PropStreamsDict.Add(group3.Key, Tuple.Create(group3.Value.StreamId,
group3.Value.Uom));
currentRequest.ItemIds.AddRange(itemIdList);
retOutput.Add(currentRequest);
}
}

You should use SelectMany to make nested foreach.
Here what I come up with:
public List<ClassA> GetLists(Dictionary<string, Dictionary<IEnumerable, Dictionary<string, ClassB>>> groups)
{
return groups
.SelectMany(grp1 => grp1.Value
.SelectMany(grp2 => grp2.Value
.SelectMany(grp3 => grp3.Value.ItemId
.IntoChunks(2000)
.Select(itemIdList =>
new ClassA
{
TransactionType = grp1.Key,
Filters = grp2.Key is Dictionary<string, object> ?
(Dictionary<string, object>)grp2.Key :
new Dictionary<string, object>(),
ItemIds = new List<string>(itemIdList),
PropStreamsDict = new Dictionary<string, Tuple<long, string>>
{
{ grp3.Key, Tuple.Create(grp3.Value.StreamId, grp3.Value.Uom) }
}
}
)
)
)
)
.ToList();
}
You didn't post your ClassA and ClassB so I had to guess.

Related

How to GroupBy List<Dictionary<string,ClassName>> by class ClassName property

I want to group by my list of dictionaries dynamically just like:
public static List<Dictionary<string, ClassName>> GroupBy(this List<Dictionary<string, ClassName>> list)
{
return list.AsQueryable().GroupBy(i => new { Key1 = i["Key01"].ClassProperty, Key2 = i["Key02"].AnotherClassProperty });
}
but I need to set keys name and numbers dynamically.
I had an idea to create dynamic-typed variable using ExpandoObject like that:
public static List<Dictionary<string, ClassName>> GroupBy(this List<Dictionary<string, ClassName>> list, IEnumerable<string> fieldnames)
{
return list.AsQueryable().GroupBy(i => GetKeys(fieldnames, dict) });
dynamic GetKeys(IEnumerable<string> fieldnames, Dictionary<string, ClassName> dict)
{
IDictionary<string, object> sampleObject = new ExpandoObject();
foreach (var fieldname in fieldnames)
{
sampleObject.Add($"Key{sampleObject.Count}", dict.GetValueOrDefault(fieldname).ClassProperty;
}
return sampleObject;
}
}

Do I really need to create and return the collection for this method?

I have seen people use yield return, although every time I try and use it in foreach it tells me something along the lines of: The body of CollectDictionary cannot be an iterator block because Dictionary<string, object> is not an interator interface type
I have a lot of methods line the following, is there a way to reduce boilerplate?
public Dictionary<string, object> CollectDictionary()
{
var configLines = File.ReadAllLines(_file).Where(IsValidConfigLine);
var configElements = new Dictionary<string, object>();
foreach (var configElement in configLines)
{
configElements.Add(configElement.Split("=")[0], configElement.Split("=")[1]);
}
return configElements;
}
You can reduce the amount of code by using .ToDictionary().
public Dictionary<string, object> CollectDictionary()
{
string[] configLines = new string[]
{
"foo1=bar1",
"foo2=bar2",
"foo3=bar3",
"foo4=bar4",
};
return configLines.Select(configElement => configElement.Split("="))
.ToDictionary(splt => splt[0], splt => (object)splt[1]);
}
This should return the following Dciotnary
{
{ "foo1", "bar1" },
{ "foo2", "bar2" },
{ "foo3", "bar3" },
{ "foo4", "bar4" }
}

Convert delimited string to Expando Object

I lave a dictionary like this -
Dictionary<string, Object> dict = new Dictionary<string, Object>();
dict.Add("event.data", "data");
dict.Add("event.Location", LocObj);
dict.Add("event.Details.Cost", CostObj);
I am trying to merge this with the existing json structure -
{
"event" : {
"name" : "Hello World",
"tags" : [tags]
}
"sponsors" : {
...
}
"OtherObj" : {
...
}
}
I am trying to use ExpandoObject and then inject it into the original json, like this-
foreach(KeyValuePair<string, Object> kvp in dict) {
var newObj= new ExpandoObject() as IDictionary<string, Object>;
newObj.Add(kvp.Key, value);
existingObject.Merge(JObject.FromObject(newObj), new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
});
}
Console.WriteLine(JsonConvert.SerializeObject(existingObject));
But when it gets serialized event.Location does not show up inside the existing event tag, instead it adds a new key called event.Location.
{
"event" : {
"name" : "Hello World",
"tags" : [tags]
},
"event.Location" : { ... }, <---------------
"event.data": { ... }, <---------------
"event.Details.Cost" : { ... }<---------------
"sponsors" : {
...
},
"OtherObj" : {
...
}
}
How do I fix the Expando obj creation to fix this?
See the below program. This will show an example of a working merge and breaking merge. The difference is how the object is structured. In one case, there is "Event.Location" which does not work. In the other case, the "Event" object contains a "Location" and it correctly nests in the JObject's merge operation.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Dynamic;
namespace DynamicSerial
{
class Program
{
static void Main(string[] args)
{
var myObject = new Dictionary<string, Object>();
myObject.Add("Id", 1);
myObject.Add("Event", new { Name = "SomeName", Id = 2 });
var mergeObject = new Dictionary<string, Object>();
mergeObject.Add("Event", new { Location = new { Name = "SomeLocation", Id = 3 } }); //WORKS
//mergeObject.Add("Event.Location", new { Name = "SomeLocation", Id = 3 }); //DOES NOT WORK
JObject myDynamicObject = JObject.FromObject(myObject);
foreach (KeyValuePair<string, Object> kvp in mergeObject)
{
var newObj = new ExpandoObject() as IDictionary<string, Object>;
newObj.Add(kvp.Key, kvp.Value);
myDynamicObject.Merge(JObject.FromObject(newObj), new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Merge
});
}
Console.WriteLine(JsonConvert.SerializeObject(myDynamicObject));
}
}
}
The resulting JSON
good:
{"Id":1,"Event":{"Name":"SomeName","Id":2,"Location":{"Name":"SomeLocation","Id":3}}}
bad:
{"Id":1,"Event":{"Name":"SomeName","Id":2},"Event.Location":{"Name":"SomeLocation","Id":3}}
In conclusion, you should change
Dictionary<string, Object> dict = new Dictionary<string, Object>();
dict.Add("event.data", "data");
dict.Add("event.Location", LocObj);
dict.Add("event.Details.Cost", CostObj);
To
Dictionary<string, Object> dict = new Dictionary<string, Object>();
dict.Add("event", new { data="data", Location=LocObj, Details=new { Cost=CostObj } });

Foreach object loop

My already code:
foreach (KeyValuePair<string, object> keyValuePair in items)
{
if (keyValuePair.Key == "animals")
{
Dictionary<string, object> animal = new Dictionary<string, object>();
animal.Add(keyValuePair.Key, keyValuePair.Value);
object animalObject = keyValuePair.Value;
foreach (??? in animalObject)
{
}
}
object fajny = keyValuePair.Value;
result2 = fajny as ArrayCollection;
}
I want to loop Dictionary elements from animalObject object:
http://scr.hu/0tgp/6dteu
http://screenshu.com/static/uploads/temporary/1h/1p/05/3p9fyc.jpg
I don't have an idea how do this. I cant search help anywhere.
You can try to cast to IEnumerable, but I would suggest trying to retain strong typing.
foreach (KeyValuePair<string, object> keyValuePair in items)
{
if (keyValuePair.Key == "animals")
{
Dictionary<string, object> animal = new Dictionary<string, object>();
animal.Add(keyValuePair.Key, keyValuePair.Value);
IEnumerable animalObject = keyValuePair.Value as IEnumerable;
if(animalEnumerable != null)
{
foreach (object animalObj in animalEnumerable)
{
}
}
}
}
are you trying to loop over the values in your dictionary: animal?
foreach (object animalObject in animal.Values)
{
}

'Convert' Dictionary<string,int> into List<object>

I have a Dictionary<string,int> dictionary1 and I need to convert it into a List<Data> where Data has the properties lable = dictionary1.key and value = dictionary1.value. I don't want to use a for/foreach loop (written by myself) because in order to avoid it I am trying to use a Dictionary.
Another option would be having two different dictionaries (dictionary2 and dictionary3) where dictionary2<string,keyOfDictionary1> and dictionary3<string,valueOfDictionary1>.
Do I make sense? Is that possible? Is there a better option?
Assuming:
class Data
{
public string Label { get; set; }
public int Value { get; set; }
}
Then:
Dictionary<string, int> dic;
List<Data> list = dic.Select(p => new Data { Label = p.Key, Value = p.Value }).ToList();
Perhaps you could use LINQ?
dictionary1.Select(p => new Data(p.Key, p.Value)).ToList()
This is however using yield and thus loops in the background...
myDictionary.Select(x => new Data(){ label = x.Key, value = x.Value).ToList();
I assume that "no loop" actually means "i want LINQ":
List<Data> = dictionary1.Select(
pair => new Data() {
label = pair.Key,
value = pair.Value
})).ToList();
public class Data
{
public string Key { get; set; }
public int Value { get; set; }
}
private static void Main(string[] args)
{
Dictionary<string, int> dictionary1 = new Dictionary<string, int>();
dictionary1.Add("key1", 1);
dictionary1.Add("key2", 2);
List<Data> data = dictionary1.Select(z => new Data { Key = z.Key, Value = z.Value }).ToList();
Console.ReadLine();
}
Try
dictionary1.Select(p => new Data(p.Key, p.Value)).ToList();
.NET already has a data type that does what Data would do: KeyValuePair<T1,T2>. Dictionary already implements IEnumerable<KeyValuePair<T1,T2>>, just cast to it.
Dictionary<string, int> blah = new Dictionary<string, int>();
IEnumerable<KeyValuePair<string, int>> foo = blah;
This is a old post, but post only to help other persons ;)
Example to convert any object type:
public List<T> Select<T>(string filterParam)
{
DataTable dataTable = new DataTable()
//{... implement filter to fill dataTable }
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary<string, object> row;
foreach (DataRow dr in dataTable.Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in dataTable.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
string json = new JavaScriptSerializer().Serialize(rows);
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T[]));
var tick = (T[])deserializer.ReadObject(stream);
return tick.ToList();
}
}
Just in case just helps anyone, I did it like this - will handle objects more complex than a single value type, as stated by the OP.
// Assumes: Dictionary<string, MyObject> MyDictionary;
List<MyObject> list = new List<MyObject>();
list.AddRange(MyDictionary.Values.ToArray());

Categories

Resources