#c# #list
#c# #Список
Вопрос:
У меня есть список, в котором каждый double[] имеет длину 3. Я хотел бы очистить этот список, оставив только те double[], которые имеют уникальные элементы в пределах заданного допуска (округление). Например, список, подобный приведенному ниже:
1059.17 0 446.542225842081
1059.17 0 446.542564789741
1059.17 0 446.541759880305
959.167 0 579.827860527898
959.167 0 579.827847296075
Должно стать таким для заданного допуска = два:
1059.17 0 446.54,
959.17 0 579.83,
Есть ли разумный способ сделать это аккуратно?
Комментарии:
1. Ну, во-первых, вы не можете хранить их как double . Вы должны хранить их как строки, поскольку количество отображаемых десятичных знаков будет зависеть от того, как вы форматируете фактическое десятичное число. Вы можете либо сделать это таким образом, либо отформатировать число везде, где вы его используете.
2. почему 959.167 не становится 959.17 ?
3. msdn.microsoft.com/en-us/library/75ks3aby (v = против 110).aspx
Ответ №1:
Это должно сработать. Он использует встроенные сравнения равенства анонимных типов.
List<double[]> data = ...
int tolerance = 2;
var roundedData = data
.Select(x => new {
v1 = Math.Round(x[0], tolerance),
v2 = Math.Round(x[1], tolerance),
v3 = Math.Round(x[2], tolerance)
})
.Distinct()
.Select(x => new [] { x.v1, x.v2, x.v3 })
.ToList();
Ответ №2:
При условии, что элементы массива всегда находятся в одном и том же порядке, вы можете создать свой собственный компаратор, который должен знать, как сравнивать двойные массивы :
public class MyDoubleArrComparer : IEqualityComparer<double[]>
{
public bool Equals(double[] x, double[] y)
{
for (int i = 0; i < x.Length; i )
{
if (x[i] != y[i]) return false;
}
return true;
}
public int GetHashCode(double[] obj)
{
return base.GetHashCode();
}
}
И вы можете создать вспомогательный метод, который будет округлять числа и удалять дубликаты :
public static class Helper
{
public static List<double[]> MyFilter(this List<double[]> list, int tolerance)
{
var result = list
.Select(arr =>
{
// rounds numbers with precision that is set in tolerance variable
arr = arr.Select(d => d = Math.Round(d, tolerance)).ToArray();
return arr;
}).Distinct(new MyDoubleArrComparer()) // here we use our custom comparer
.ToList();
return resu<
}
}
Теперь мы можем начать использовать наш вспомогательный метод :
var nums = new List<double[]>()
{
new[] {1059.17, 0, 446.542225842081},
new[] {1059.17, 0, 446.542564789741},
new[] {1059.17, 0, 446.541759880305},
new[] {959.167, 0, 579.827860527898},
new[] {959.167, 0, 579.827847296075},
};
var result = nums.MyFilter(2);
foreach (var arr in result)
{
foreach (var d in arr)
{
Console.Write(d " ");
}
Console.WriteLine();
}
Вывод :
1059.17 0 446.54
959.17 0 579.83
Ответ №3:
Может быть, это сработает?
public List<double[]> CleanWithTolerance(List<double[]> doubleNumbersList, int tolerance)
{
var newDoublesNumbersList = new List<double[]>();
foreach(double[] doubleNumbers in doubleNumbersList)
{
var newDoublesNumbers = doubleNumbers.Select(doubleNumber => Math.Round(doubleNumber, tolerance)).ToArray();
if(newDoublesNumbersList.All(cleanDoubleNumbers => !Enumerable.SequenceEqual(cleanDoubleNumbers, newDoublesNumbers))
{
newDoublesNumbersList.Add(newDoublesNumbers);
}
}
return newDoublesNumbersList;
}
Комментарии:
1. Математика. Округление будет округляться до ближайшего, а не округляться вверх. Математика. Потолок можно использовать для округления таким
Math.Ceiling(doubleNumber*x)/x
образом, где x равно допуску 10 ^.2. В примере 446,541759880305 было округлено до 446,54 и для этой математики. Round — это просто отлично.
3. В тексте говорится «заданный допуск (округление)». Поэтому я думаю, что вопрос непоследователен.