How to skillfully create an anonymous type from IEnumerable <T>?

I would like to use LINQ to solve the following problem: I have the following collection:

List<byte> byteList = new List<byte() { 0x01, 0x00, 0x01, 0x02, 0x01, 0x00, 0x3, 0x4, 0x02 }; 

The data in this example follows the following pattern:

byteList [0] = address (1, 2, 3, ... n)

byteList [1] = old state, which is basically an enumeration

byteList [2] = new state, same as above

I interact with the embedded device and I can view the changes in the inputs.

To clear the code and make it easier for the maintenance programmer following my logic, I would like to divert some of the involved nuts and bolts and extract each three-byte data set into an anonymous type so that it can be used in the function to do some extra processing. I wrote a brief implementation, but I'm sure it can be greatly simplified. I'm trying to clear the code, not muddy water! There should be an easier way to do the following:

 List<byte> byteList = new List<byte>() { 0x01, 0x09, 0x01, 0x02, 0x08, 0x02, 0x03, 0x07, 0x03 }; var addresses = byteList .Where((b, i) => i % 3 == 0) .ToList(); var oldValues = byteList .Where((b, i) => i % 3 == 1) .ToList(); var newValues = byteList .Where((b, i) => i % 3 == 2) .ToList(); var completeObjects = addresses .Select((address, index) => new { Address = address, OldValue = oldValues[index], NewValue = newValues[index] }) .ToList(); foreach (var anonType in completeObjects) { Console.WriteLine("Address: {0}\nOld Value: {1}\nNew Value: {2}\n", anonType.Address, anonType.OldValue, anonType.NewValue); } 
+4
source share
6 answers

You can use Enumerable.Range and a bit of math:

 List<byte> byteList = new List<byte>() { 0x01, 0x09, 0x01, 0x02, 0x08, 0x02, 0x03, 0x07, 0x03 }; var completeObjects = Enumerable.Range(0, byteList.Count / 3).Select(index => new { Address = byteList[index * 3], OldValue = byteList[index * 3 + 1], NewValue = byteList[index * 3 + 2], }); 

If the number of bytes is not a multiple of 3, the extra one or two bytes will be ignored.

+5
source

For simplicity, I will create a record type and use a for loop:

 class RecordType { //constructor to set the properties omitted public byte Address { get; private set; } public byte OldValue { get; private set; } public byte NewValue { get; private set; } } IEnumerable<RecordType> Transform(List<byte> bytes) { //validation that bytes.Count is divisible by 3 omitted for (int index = 0; index < bytes.Count; index += 3) yield return new RecordType(bytes[index], bytes[index + 1], bytes[index + 2]); } 

Alternatively, if you definitely need an anonymous type, you can do this without linq:

 for (int index = 0; index < bytes.Count; index += 3) { var anon = new { Address = bytes[index], OldValue = bytes[index + 1], NewValue = bytes[index + 3] }; //... do something with anon } 

Linq is very useful, but it is inconvenient for this task, because the elements of the sequence have a different meaning depending on their location in the sequence.

+3
source

I'm not sure if this qualifies as a smart solution, but I used this example to try to accomplish this without creating separate lists.

 var completeObjects = byteList // This is required to access the index, and use integer // division (to ignore any reminders) to group them into // sets by three bytes in each. .Select((value, idx) => new { group = idx / 3, value }) .GroupBy(x => x.group, x => x.value) // This is just to be able to access them using indices. .Select(x => x.ToArray()) // This is a superfluous comment. .Select(x => new { Address = x[0], OldValue = x[1], NewValue = x[2] }) .ToList(); 
0
source

If you should use LINQ (not sure if this is a good plan), then one of the options is:

 using System; using System.Collections.Generic; using System.Linq; static class LinqExtensions { public static IEnumerable<T> EveryNth<T>(this IEnumerable<T> e, int start, int n) { int index = 0; foreach(T t in e) { if((index - start) % n == 0) { yield return t; } ++index; } } } class Program { static void Main(string[] args) { List<byte> byteList = new List<byte>() { 0x01, 0x09, 0x01, 0x02, 0x08, 0x02, 0x03, 0x07, 0x03 }; var completeObjects = byteList.EveryNth(0, 3).Zip ( byteList.EveryNth(1, 3).Zip ( byteList.EveryNth(2, 3), Tuple.Create ), (f,t) => new { Address = f, OldValue = t.Item1, NewValue = t.Item2 } ); foreach (var anonType in completeObjects) { Console.WriteLine("Address: {0}\nOld Value: {1}\nNew Value: {2}\n", anonType.Address, anonType.OldValue, anonType.NewValue); } } } 
0
source

How about this?

 var addresses = from i in Enumerable.Range(0, byteList.Count / 3) let startIndex = i * 3 select new { Address = byteList[startIndex], OldValue = byteList[startIndex + 1], NewValue = byteList[startIndex + 2] }; 

Note. I developed this regardless of Michael Liu's answer, and although its almost the same, I'm going to leave this answer here because it looks prettier to me. :-)

0
source

This is an attempt to do this using the ChunkToList extension ChunkToList , which splits IEnumerable<T> into pieces of IList<T> .

Using:

  var compObjs = byteList.ChunkToList(3) .Select(arr => new { Address = arr[0], OldValue = arr[1], NewValue = arr[2] }); 

Implementation:

 static class LinqExtensions { public static IEnumerable<IList<T>> ChunkToList<T>(this IEnumerable<T> list, int size) { Debug.Assert(list.Count() % size == 0); int index = 0; while (index < list.Count()) { yield return list.Skip(index).Take(size).ToList(); index += size; } } } 
0
source

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


All Articles