2010-06-21 55 views
2

我似乎對此有點卡住,但我在Linq的經歷並不好。基本上,我有這樣的事情代碼:Linq加入Xs,Ys入點

public class Point 
{ 
    public int X { get; set; } 
    public int Y { get; set; } 
} 

public class B 
{ 
    public List<Point> Points { get; set; } 
    public B(IEnumerable<int> Xs, IEnumerable<int> Ys) 
    { 
     // How to best combine Xs and Ys into Points ? 
    } 
} 

現在,我該如何填寫該構造適當加入了這些集合?我通常會使用.Join(),但沒有內鍵可以加入。它也應該能夠處理一個數組比另一個數組少的元素或爲空的機會(絕不應該發生,但這是可能的)。

+0

你不知道它,但你正在尋找一個「zip」函數。這是我最喜歡的鏈接:[如何在C#中執行Python的zip?](http://stackoverflow.com/questions/2427015/how-to-do-pythons-zip-in-c) – Gabe 2010-06-21 19:45:20

+0

這就是爲什麼微軟添加了將.NET擴展方法擴展到.NET 4.0。 – Steven 2010-06-21 19:46:27

回答

4

在C#4你可以使用Zip operator

var result = Xs.Zip(Ys, (a,b) => new Point(a,b)); 

在C#3,您可以用選擇的序列中的一個使用ElementAt做同樣的事情:

var result = Xs.Select((x,i) => new Point(x, Ys.ElementAt(i)); 

主要第二個選項的問題在於,如果IEnumerable集合本身是一個投影(與實現本機索引操作的東西相反,如數組或列表),則ElementAt可能非常昂貴。您可以通過先迫使第二集合成爲一個列表繞開這個問題:

var YsAsList = Ys.ToList(); 
var result = Xs.Select((x,i) => new Point(x, YsAsList.ElementAt(i)); 

你必須處理(如果你不使用Zip)的另一個問題是如何處理不平衡的集合。如果一個序列長於另一個序列,則必須決定正確的分辨率。您的選擇是:

  1. 不支持它。失敗或中止嘗試。
  2. 對較短序列中存在的項目進行壓縮。
  3. 對較長序列中存在的項目進行壓縮,將短序列的默認值替換。

如果您使用Zip(),則自動結束與第二選項,因爲文檔指示:

的方法合併所述第一序列的每個元素與具有相同的元件 索引在第二個 序列中。如果序列中沒有相同數量的元素,則 方法會合並序列,直到其 到達其中一個元素的末尾。例如,如果一個序列有三個 元素,另一個序列有四個,則結果序列將只有 三個元素。

所有這一切的最後一步是您需要將項目結果轉換爲列表,以將其分配給您的Points對象。那部分是容易的,使用ToList()方法:

Points = Xs.Select((x,i) => new Point(x, Ys.ElementAt(i)).ToList(); 

或C#4:

Points = Xs.Zip(Ys, (a,b) => new Point(a,b)).ToList(); 
+0

請注意,這僅限於.Net 4.0。 – Gabe 2010-06-21 19:46:56

+0

非常感謝。這似乎正是我正在尋找的。我對Zip做了一個不好的假設(不正確地處理不相等的尺寸)。我正在使用C#4,所以這很好用! – drharris 2010-06-21 19:51:59

+0

你可以在C#3.5及更低版本中自己編寫Zip。 – Jodrell 2014-10-30 17:12:08

1

據推測,應該是一個錯誤,如果這兩個集合是不相同的尺寸,或者如果一個或兩個爲空。

public B(IEnumerable<int> Xs, IEnumerable<int> Ys) 
{ 
    if (Xs == null || Ys == null) 
     throw new ArgumentNullException("Both collections need to be non-null"); 

    if (Xs.Count() != Ys.Count()) 
     throw new ArgumentException("Collections must be of the same size"); 

    this.Points = Xs.Zip((x,y) => new Point { X = x, Y = y }).ToList(); 
} 

如果你需要.NET 3.5的東西,用最後一行代替。

this.Points = Xs.Select((x,i) => new Point { X = x, Y = Ys.ElementAt(i) }).ToList(); 
+0

是的,我會手動檢查空值和空值。如果它們不相等,我寧願將所有數據都提交到這個不相等的空間。 – drharris 2010-06-21 19:50:52

+0

@drharris - 我認爲Zip會做你想做的。如果你需要它與3.5一起工作,那麼使用'Xs.Take(Ys.Count()).Select(...)' - 這樣,如果有更多的X比Ys多,你只能使用第一個Ys-從X計算元素。如果Y比X多,則只會返回少於請求的元素。 – tvanfosson 2010-06-21 19:57:36