У меня есть два массива, удваивается того же размера, containg значения X и Y для некоторых графиков.
Я должен создать некоторую защиту от значений INF/NaN. Я должен найти все это парами значений (X, Y), для которого оба, X и Y не являются INF, ни NaN
Если у меня есть один массив, я могу сделать это с помощью лямбд:
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.
Небольшая поправка к исходному ответу Марка:
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();
В 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)
Вы можете попробовать это сделать:
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
> DualWhere (this IEnumerable one, IEnumerable two, Func 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 (); {{ 1}} twoEnumerator.Dispose ();
Изменить: Последний должен работать с C # 3.5 .
Хорошо, значит вы не можете использовать .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
Вот решение, которое будет работать в 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 & 9.4 и 17.8 & 26.2. Средние две пары будут отброшены, поскольку одна содержит NaN, а другая - бесконечность.
foreach (var pair in query)
{
Console.WriteLine("{0}\t{1}", pair.X, pair.Y);
}