Split collection into objects based on condition and occurrence

This is the following array (each block [] represents an entry):

[A=1] [A=5] [S=3] [A=7] [C=3] [T=2] [F=9] [Z=4] [N] [C=3] [E=8] [A=7] [N] [Z=6] [Q=1] [P=2] [Y=7] [S=3] [N] 

I need to break it into objects of type "N" (NObject), where each other character represents a specific property of this NObject until the next occurrence of "N". Until the first appearance of "N" characters belong to another object (let's call it PObject). Therefore, tasks should be performed as follows:

  • Map each character to the PObject property
  • When the first "N" happens, create a new NObject
  • Match each character property of this NObject
  • If another character N occurs, create a new NObject

Currently, in pseudo-code, my solution is as follows, which I consider far from ideal.

 PObject pobject = new PObject(); NObject nobject; CollectionOfKeyValuePairs collection = MyArray.Split('=').MapKeysValues() foreach(entry in collection) { switch(entry.Key): case A: (nobject ?? (CommonBase) pobject).A += entry.Value; break; case B: (nobject ?? (CommonBase) pobject).B += entry.Value; break; case C: (nobject ?? (CommonBase) pobject).C += entry.Value; break; case E: pobject.E += entry.Value; break; case F: (nobject ?? (CommonBase) pobject).F += entry.Value; break; case G: (nobject ?? (CommonBase) pobject).G += entry.Value; break; case H: (nobject ?? (CommonBase) pobject).H += entry.Value; break; ... ... ... case N: nobject = new NObject(); .... .... } } 

This gives me exactly what I want:

 [pobject] A = 23 B = 63 C = 23 ... [nobject] A = 34 B = 82 C = 12 ... [nobject] H = 236 K = 2 ... [nobject] // N occurred in array, but no properties followed 

But with over 30 possible property identifiers (which means 30 switch conditions) and a property assigned only based on the fact that nobject can be null (and creating a new β€œN” char event): the code is incredibly smelly. But I don’t know how to do this, maybe with the built-in functions of the collection, LINQ or something else.

+6
source share
2 answers

You can use a dictionary to store key-value pairs instead of explicitly creating properties for each possible case. Sort of:

 List<Dictionary<char,int>> listOfPNObjects = new List<Dictionary<char,int>>(); listOfPNObjects.Add(new Dictionary<char,int>()) //create default P dictionary foreach(entry in collection) { if(entry.Key == N) { listOfPNObjects.Add(new Dictionary<char,int>()); } else { listOfPNObjects[listOfPNObjects.Count - 1].Add(entry.key, entry.value); } } 
+1
source

I rewrote your code using reflection and LINQ:

 var objects = keyValuePairList .Aggregate<KeyValuePair<string, dynamic>, List<CommonBase>>( new List<CommonBase>(), (a, p) => { CommonBase cObject; if (p.Key == "N") { cObject = new NObject(); a.Add(cObject); } if (a.Count == 0) { cObject = new PObject(); Process(p, ref cObject); a.Add(cObject); } else { cObject = a.Last(); Process(p, ref cObject); } return a; }); 

Inside the Process method, you can process the processing of properties depending on their type:

 private static void Process( KeyValuePair<string, dynamic> kvPair, ref CommonBase cObject) { var propertyInfo = typeof(CommonBase).GetProperty(kvPair.Key); switch (propertyInfo.PropertyType.FullName) { case "System.Int32": propertyInfo .SetValue(cObject, (int)propertyInfo.GetValue(cObject) + (int)kvPair.Value); break; } } 
+1
source

Source: https://habr.com/ru/post/950877/


All Articles