У меня есть список 'двойных' значений. Я должен выбрать каждую 6-ю запись. Это - список координат, где я должен получить минимальное и максимальное значение каждого 6-го значения.
Список координат (образец): [2.1, 4.3, 1.0, 7.1, 10.6, 39.23, 0.5, ... ]
с hundrets координат.
Результат должен быть похожим: [x_min, y_min, z_min, x_max, y_max, z_max]
точно с 6 координатами.
После работ кода, но это берет к долго для итерации по всем координатам. Я хотел бы использовать Linq вместо этого (возможно, быстрее?)
for (int i = 0; i < 6; i++)
{
List<double> coordinateRange = new List<double>();
for (int j = i; j < allCoordinates.Count(); j = j + 6)
coordinateRange.Add(allCoordinates[j]);
if (i < 3) boundingBox.Add(coordinateRange.Min());
else boundingBox.Add(coordinateRange.Max());
}
Какие-либо предложения?Большое спасибо! Приветствует!
coordinateRange.Where( ( coordinate, index ) => (index + 1) % 6 == 0 );
Ответ от Webleeuw был опубликован до этого, но IMHO яснее использовать индекс в качестве аргумента вместо использования метода IndexOf
.
Что-то вроде этого может помочь:
public static IEnumerable<T> Every<T>(this IEnumerable<T> source, int count)
{
int cnt = 0;
foreach(T item in source)
{
cnt++;
if (cnt == count)
{
cnt = 0;
yield return item;
}
}
}
Вы можете использовать это так:
int[] list = new []{1,2,3,4,5,6,7,8,9,10,11,12,13};
foreach(int i in list.Every(3))
{ Console.WriteLine(i); }
EDIT :
Если вы хотите пропустить первые несколько записей, вы можете использовать метод расширения Skip ():
foreach (int i in list.Skip(2).Every(6))
{ Console.WriteLine(i); }
Есть какая-то конкретная причина, по которой вы хотите использовать LINQ для этого?
Почему бы не написать цикл, который шагает каждый раз с 6 приращениями и получает доступ к значению напрямую?
Лучшим способом сделать это будет рефакторинг структуры данных, чтобы каждое измерение было отдельным массивом. Таким образом, x1_max
будет просто x1.Max ()
. Если вы не можете изменить структуру входных данных, следующий лучший способ - выполнить итерацию по массиву один раз и выполнить все обращения локально. Это помогает оставаться в кэшированной памяти L1:
var minValues = new double[] { Double.MaxValue, Double.MaxValue, Double.MaxValue };
var maxValues = new double[] { Double.MinValue, Double.MinValue, Double.MinValue };
for (int i = 0; i < allCoordinates.Length; i += 6)
{
for (int j = 0; i < 3; i++)
{
if (allCoordinates[i+j] < minValues[j])
minValues[j] = allCoordinates[i+j];
if (allCoordinates[i+j+3] > maxValues[j])
maxValues[j] = allCoordinates[i+j+3];
}
}
Ну, это не LINQ, но если вы беспокоитесь о производительности, это может помочь.
public static class ListExtensions
{
public static IEnumerable<T> Every<T>(this IList<T> list, int stepSize, int startIndex)
{
if (stepSize <= 0)
throw new ArgumentException();
for (int i = startIndex; i < list.Count; i += stepSize)
yield return list[i];
}
}
Существует перегрузка метода Where, позволяющая использовать индекс напрямую:
coordinateRange.Where((c,i) => (i + 1) % 6 == 0);
Чтобы найти более быстрое решение заведите профиль!
Измерьте, сколько времени требуется на каждый шаг в вашем цикле for, и постарайтесь избежать самого узкого места.
После повторного взгляда на ваш код, кажется, что ваша проблема в том, что вы шесть раз пробегаетесь по большому списку. Поэтому необходимое время всегда в шесть раз больше размера списка.
Вместо этого вы должны один раз прогнать весь список и поместить каждый элемент в нужный слот.
Чтобы проверить свою производительность, вам следует проверить эти два подхода:
public class Coordinates
{
public double x1 { get; set; }
public double x2 { get; set; }
public double y1 { get; set; }
public double y2 { get; set; }
public double z1 { get; set; }
public double z2 { get; set; }
}
Coordinates minCoordinates = new Coordinates();
//Cause we want to hold the minimum value, it will be initialized with
//value that is definitely greater or equal than the greatest in the list
minCoordinates.x1 = Double.MaxValue;
minCoordinates.x2 = Double.MaxValue;
minCoordinates.y1 = Double.MaxValue;
minCoordinates.y2 = Double.MaxValue;
minCoordinates.z1 = Double.MaxValue;
minCoordinates.z2 = Double.MaxValue;
for (int i = 0; i < allCoordinates.Count; i++)
{
switch (i % 6)
{
case 0:
minCoordinates.x1 = Math.Min(minCoordinates.x1, allCoordinates[i]);
break;
case 1:
minCoordinates.x2 = Math.Min(minCoordinates.x2, allCoordinates[i]);
break;
case 2:
minCoordinates.y1 = Math.Min(minCoordinates.y1, allCoordinates[i]);
break;
case 3:
minCoordinates.y2 = Math.Min(minCoordinates.y2, allCoordinates[i]);
break;
case 4:
minCoordinates.z1 = Math.Min(minCoordinates.z1, allCoordinates[i]);
break;
case 5:
minCoordinates.z2 = Math.Min(minCoordinates.z2, allCoordinates[i]);
break;
}
}
int count = 0;
foreach (var item in allCoordinates)
{
switch (count % 6)
{
case 0:
minCoordinates.x1 = Math.Min(minCoordinates.x1, item);
break;
case 1:
minCoordinates.x2 = Math.Min(minCoordinates.x2, item);
break;
case 2:
minCoordinates.y1 = Math.Min(minCoordinates.y1, item);
break;
case 3:
minCoordinates.y2 = Math.Min(minCoordinates.y2, item);
break;
case 4:
minCoordinates.z1 = Math.Min(minCoordinates.z1, item);
break;
case 5:
minCoordinates.z2 = Math.Min(minCoordinates.z2, item);
break;
}
count++;
}
Предложение:
coordinateRange.Where(c => (coordinateRange.IndexOf(c) + 1) % 6 == 0);
Я исправлен, спасибо за комментарии. Как сказал codymanix, правильный ответ действительно таков:
coordinateRange.Where((c, i) => (i + 1) % 6 == 0);