[英]How to cleverly create an anonymous type from an IEnumerable<T>?
我想用LINQ來解決以下問題,我有以下集合:
List<byte> byteList = new List<byte() { 0x01, 0x00, 0x01, 0x02, 0x01, 0x00, 0x3, 0x4, 0x02 };
此示例中的數據遵循以下模式:
byteList [0] =地址(1,2,3,... n)
byteList [1] =舊狀態,基本上代表枚舉
byteList [2] =新狀態,與上面相同
我正在與嵌入式設備連接,這就是我可以查看輸入變化的方式。
為了清理代碼並使維護程序員更容易遵循我的邏輯,我想抽象出所涉及的一些細節,並將每個三字節數據集提取為一個匿名類型,以便在其中使用執行一些額外處理的功能。 我寫了一個快速實現,但我相信它可以大大簡化。 我正在努力清理代碼,而不是泥濘的水域! 必須有一種更簡單的方法來執行以下操作:
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);
}
你可以使用Enumerable.Range
和一些小數學:
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],
});
如果字節數不是3的倍數,則將忽略額外的一個或兩個字節。
為簡化起見,我創建了一個記錄類型並使用for
循環:
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]);
}
或者,如果您確實需要匿名類型,則可以在沒有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非常有用,但在這項任務中很尷尬,因為序列項具有不同的含義,具體取決於它們在序列中的位置。
我不確定這是否是一個聰明的解決方案,但我使用該示例嘗試在不創建單獨列表的情況下完成此操作。
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();
如果你必須使用LINQ(不確定它是一個好的計划),那么一個選項是:
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);
}
}
}
這個怎么樣?
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]
};
注意:我獨立於Michael Liu的答案開發了這個,雖然他幾乎是一樣的,但我會在這里留下這個答案,因為它看起來更漂亮。 :-)
這是嘗試使用擴展方法ChunkToList
,它將IEnumerable<T>
拆分為IList<T>
的塊。
用法:
var compObjs = byteList.ChunkToList(3)
.Select(arr => new {
Address = arr[0],
OldValue = arr[1],
NewValue = arr[2]
});
執行:
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;
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.