用Linq找出有搭到的項目
有個需求如下:
- 原始資料來源有 1 ~ 10。
- 另一個資料表有兩個欄位,儲存多筆起迄區間,例如:2~3、3~5、8~9。
- 要找出原始來源中,和起迄區間有交集的項目。
用 Linqpad 的 C# Statement 模式,弄個範例程式如下:
var list = Enumerable.Range(1, 10);
DataTable dt = new DataTable();
dt.Columns.Add("Begin", System.Type.GetType("System.Int32"));
dt.Columns.Add("End", System.Type.GetType("System.Int32"));
dt.Rows.Add(2, 3);
dt.Rows.Add(3, 5);
dt.Rows.Add(8, 9);
dt.Dump();
var q1= (from x in dt.AsEnumerable()
select Enumerable.Range(x.Field<int>("Begin"), x.Field<int>("End")-x.Field<int>("Begin")+1))
.SelectMany (v => v);
var query = list.Intersect(q1);
query.Count().Dump();
query.Dump();
想法是,先把所有可以交集的區間展開(Enumerable.Rang )、扁平化(SelectMany),然後用交集(Intersect)函數即可。特別注意的是,Enumerable.Rang 的第二個參數,是 Count,不是「迄」:
public static IEnumerable<int> Range(
int start,
int count
)
參數
start
型別: System.Int32
序列中第一個整數的值。
count
型別: System.Int32
要產生的循序整數數目。
傳回值
型別: System.Collections.Generic.IEnumerable <Int32>
C# 中的 IEnumerable<Int32> 或 Visual Basic 中的 IEnumerable(Of Int32),其中包含循序整數的範圍。
以上範例是用 int 來做,所以可以用 Enumerable.Range 來展開,若是非 int 項目,就自訂展開的方法來處理,但精神相同。
=======補充另外兩種版本========
直接 join + where 版:
var q2 = (from i in list
from x in dt.AsEnumerable()
where i >= x.Field<int>("Begin") && i <= x.Field<int>("End")
select i).Distinct();
用匿名方法:
Func<int, int, int, bool> isMatch = (i, iBegin, iEnd) => (i >= iBegin && i <= iEnd);
var q2 = (from i in list
from x in dt.AsEnumerable()
where isMatch(i, x.Field<int>("Begin"), x.Field<int>("End"))
select i).Distinct();
--------
沒什麼特別的~
不過是一些筆記而已