过滤两个数组以避免Inf / NaN值

时间:2010-05-07 16:26:41

标签: c# linq lambda

我有两个相同大小的双精度数组,包含一些情节的X和Y值。

我需要针对INF / NaN值创建某种保护。我需要找到所有的值对(X,Y),其中X和Y都不是INF也不是NaN

如果我有一个数组,我可以使用lambdas:

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

现在,对于两个数组,我使用以下循环:

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]);
     }
}

有没有一种方法可以使用LINQ / lambdas同时过滤两个数组,就像单个数组一样?

不幸的是我只能使用.NET 3.5。

5 个答案:

答案 0 :(得分:6)

Mark的原始答案稍作修正:

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();

或者,你可能想让它更整洁:

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();

如果您需要将结果返回到两个列表中,您可以执行以下操作:

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

答案 1 :(得分:2)

C#4.0引入了Enumerable.Zip扩展方法,以便在您描述时“并行”完成对可枚举的迭代。

我自己没有使用它,但它应该是这样的:

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();

(抱歉有趣的缩进,希望它在SO上更具可读性)

答案 2 :(得分:2)

好的,所以你不能使用.NET 4.0,因此不能使用Zip扩展。

可以你?

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);
    }
}

亲眼看看:)

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);
}

输出:

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

答案 3 :(得分:0)

您可以尝试这样做:

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#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();
}

编辑后者应与C#3.5配合使用。

答案 4 :(得分:0)

这是一个适用于C#3和.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 };

迭代查询将生成包含1.2&amp;的对。 9.4和17.8&amp; 26.2。中间两对将被丢弃,因为一个包含NaN而另一个包含无穷大。

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