2010-05-07 46 views
5

Tôi có hai mảng có cùng kích thước, tiếp nối các giá trị X và Y cho một số ô.Lọc hai mảng để tránh các giá trị Inf/NaN

Tôi cần tạo một số loại bảo vệ chống lại giá trị INF/NaN. Tôi cần phải tìm tất cả những gì các cặp giá trị (X, Y) mà cả hai, X và Y không INF cũng không NaN

Nếu tôi có một mảng, tôi có thể làm điều đó bằng lambdas:

var filteredValues = someValues.Where(d=> !(double.IsNaN(d) || double.IsInfinity(d))).ToList(); 

Bây giờ, đối với hai mảng, tôi sử dụng vòng lặp sau:

List<double> filteredX=new List<double>(); 
List<double> filteredY=new List<double>(); 

for(int i=0;i<XValues.Count;i++) 
{ 
    if(!double.IsNan(XValues[i]) && 
     !double.IsInfinity(XValues[i]) && 
     !double.IsNan(YValues[i]) && 
     !double.IsInfinity(YValues[i])) 
    { 
     filteredX.Add(XValues[i]); 
     filteredY.Add(YValues[i]); 
    } 
} 

Có cách lọc hai mảng cùng một lúc bằng LINQ/lambdas, vì nó được thực hiện cho một mảng không?

Rất tiếc, tôi chỉ có thể sử dụng .NET 3.5.

+0

lỗi nhẹ trong việc kê khai của mình. filterX được khai báo hai lần. Tôi chắc chắn bạn có nghĩa là lọc cho một trong số họ. : D –

+0

Cảm ơn bạn! :) Đã chỉnh sửa. – Gacek

+0

Những gì bạn tìm kiếm là khá khả thi trong .NET 3.5. Xem câu trả lời của tôi. –

Trả lời

5

chỉnh nhẹ cho Mark câu trả lời ban đầu:

var filteredValues = XValues.Zip(YValues, (x,y) => new { x, y }) 
     .Where(p => !(double.IsNan(p.x) || double.IsNan(p.y) || 
         double.IsInfinity(p.x) || double.IsInfinity(p.y))) 
     .ToList(); 

Ngoài ra, bạn có thể muốn làm cho nó một chút neater:

Func<double, bool> valid = z => !double.IsNan(z) && !double.IsInfinity(z); 
var filteredValues = XValues.Zip(YValues, (x,y) => new { x, y }) 
     .Where(p => valid(p.x) && valid(p.y)) 
     .ToList(); 

Nếu sau đó bạn cần kết quả trở lại thành hai danh sách, bạn có thể làm:

var filteredX = filteredValues.Select(p => p.x).ToList(); 
var filteredY = filteredValues.Select(p => p.y).ToList(); 
+0

Tôi đoán có nên là IsInfinity thay vì IsNan thứ hai trong tuyên bố Func hợp lệ của bạn? Rất tiếc, tôi không thể sử dụng 4.0 (xem chỉnh sửa), nhưng dù sao đi nữa cũng xin cảm ơn – Gacek

+0

@Gacek: Vâng, đó phải là IsInfinity; đã sửa. Bạn cũng có thể nhận được toán tử Zip từ MoreLINQ: http://morelinq.googlecode.com –

+0

Cảm ơn, tôi có thể sẽ làm như vậy. Cảm ơn sự giúp đỡ của bạn. – Gacek

2

C# 4.0 giới thiệu phương pháp mở rộng Enumerable.Zip để thực hiện lặp qua các tập "song song" như bạn mô tả.

tôi đã không sử dụng nó bản thân mình, nhưng nó phải là một cái gì đó như:

var filteredValues = 
    XValues.Zip(YValues, (x,y) => new { X = x, Y = y}) 
     .Where(o => 
      !(double.IsNan(o.X) || double.IsNan(o.Y) || double.IsInfinity(o.X) || double.IsInfinity(o.Y))) 
      .ToList(); 

(Xin lỗi vì sự thụt đầu dòng buồn cười, muốn nó để có thể đọc thêm về SO)

+1

Zip không có biến vị ngữ trong nó, chỉ là một lambda chuyển đổi x và y thành một số giá trị duy nhất. – Rubys

+0

Đẹp nhất. Tôi ước tôi có thể sử dụng 4.0 (xin lỗi tôi quên viết nó trong câu hỏi của tôi). – Gacek

+0

@Gacek: Không có gì ngăn bạn viết phần mở rộng * của riêng bạn * 'Zip'! Xem câu trả lời của tôi;) –

0

Bạn có thể thử để làm điều này:

doubleArray1.Zip(doubleArray2, (x, y) => Tuple.Create(x, y)) 
      .Where(tup => !double.IsNaN(tup.Item1) && 
      !double.IsNaN(tup.Item2) && 
      !double.IsInfinity(tup.Item1) && 
      !double.IsInfinity(tup.Item1)); 

Cách khác, bạn có thể làm một phương pháp lọc và nén cùng một lúc, lợi ích chính là bạn không bị giới hạn ở C# 4:

public static IEnumerable<Tuple<TOne, TTwo>> DualWhere<TOne, TTwo>(this IEnumerable<TOne> one, IEnumerable<TTwo> two, Func<TOne, TTwo, bool> predicate) 
{ 
    var oneEnumerator = one.GetEnumerator(); 
    var twoEnumerator = two.GetEnumerator(); 
    while (oneEnumerator.MoveNext() && twoEnumerator.MoveNext()) 
    { 
     if (predicate(oneEnumerator.Current, twoEnumerator.Current)) 
      yield return Tuple.Create(oneEnumerator.Current, twoEnumerator.Current); 
    } 
    oneEnumerator.Dispose(); 
    twoEnumerator.Dispose(); 
} 

Chỉnh sửa: Cách sau phải hoạt động với C# 3.5.

+0

Tôi nghĩ bạn có nghĩa là .NET 3.5. Không có thứ gì như C# 3.5. Bạn nên sử dụng bằng cách sử dụng các câu lệnh thay vì gọi thủ công Vứt bỏ mặc dù. Lưu ý rằng không có gì trong các câu trả lời hiện có yêu cầu * C# 4 * - nó chỉ yêu cầu * .NET 4 *, cũng như câu trả lời của bạn - bởi vì nó sử dụng Tuple. –

0

Dưới đây là một giải pháp mà sẽ làm việc trong C# 3 và .NET 3,5

List<double> list1 = new List<double>() { 1.2, 3.8, double.NaN, 17.8 }; 
List<double> list2 = new List<double>() { 9.4, double.PositiveInfinity, 10.4, 26.2 }; 

var query = from x in list1.Select((item, idx) => new { item, idx }) 
      where !double.IsNaN(x.item) && !double.IsInfinity(x.item) 
      join y in list2.Select((item, idx) => new { item, idx }) 
      on x.idx equals y.idx 
      where !double.IsNaN(y.item) && !double.IsInfinity(y.item) 
      select new { X = x.item, Y = y.item }; 

iterating trên các truy vấn sẽ tạo ra cặp chứa 1,2 & 9,4 và 17,8 & 26.2. Hai cặp giữa sẽ bị loại bỏ bởi vì một cặp chứa NaN và cái kia chứa vô cùng.

foreach (var pair in query) 
{ 
    Console.WriteLine("{0}\t{1}", pair.X, pair.Y); 
} 
2

OK, vì vậy bạn không thể sử dụng .NET 4.0 và do đó không thể sử dụng phần mở rộng Zip.

Hoặc có thể bạn?

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first, 
    IEnumerable<TSecond> second, 
    Func<TFirst, TSecond, TResult> resultSelector) 
{ 
    using (var eFirst = first.GetEnumerator()) 
    using (var eSecond = second.GetEnumerator()) 
    { 
     while (eFirst.MoveNext() && eSecond.MoveNext()) 
      yield return resultSelector(eFirst.Current, eSecond.Current); 
    } 
} 

Xem cho chính mình :)

static void Main(string[] args) 
{ 
    var x = new double[] { 0.0, 1.0, 2.0, double.NaN, 4.0, 5.0 }; 
    var y = new double[] { 0.5, 1.5, double.PositiveInfinity, 3.5, 4.5, 5.5 }; 

    // note: using KeyValuePair<double, double> -- 
    // you could just as easily use your own custom type 
    // (probably a simple struct) 
    var zipped = x.Zip(y, (a, b) => new KeyValuePair<double, double>(a, b)) 
     .Where(kvp => IsValid(kvp.Key) && IsValid(kvp.Value)) 
     .ToList(); 

    foreach (var z in zipped) 
     Console.WriteLine("X: {0}, Y: {1}", z.Key, z.Value); 
} 

static bool IsValid(double value) 
{ 
    return !double.IsNaN(value) && !double.IsInfinity(value); 
} 

Output:

X: 0, Y: 0.5 
X: 1, Y: 1.5 
X: 4, Y: 4.5 
X: 5, Y: 5.5 
+0

Cảm ơn, ví dụ tuyệt vời về tạo tiện ích mở rộng. Nó có thể giúp tôi một số ngày;) Cảm ơn bạn đã giúp đỡ của bạn! – Gacek

Các vấn đề liên quan