Я работаю над этой проблемой уже пару дней и не могу получить точный результат, который я ищу. Я упростил свой вопрос до простого числа, чтобы я мог максимально четко понять, что именно я делаю и что хочу взамен.
Я начинаю с большого List<List<double>>, где каждый дополнительный List в большем List содержит 3 числа. Например, List выглядит примерно так:
[0] 1,2,3
[1] 1,2,3
[2] 1,2,3
[3] 4,5,6
[4] 4,5,6
[5] 4,5,6
[6] 7,8,9
[7] 7,8,9
[8] 7,8,9
где каждый элемент в списке представляет собой отдельную последовательность. Я пытаюсь разделить List на группу меньших списков, где все элементы в списке похожи. Итак, для данного примера:
list1:
[0] 1,2,3
[1] 1,2,3
[2] 1,2,3
список 2:
[0] 4,5,6
[1] 4,5,6
[2] 4,5,6
список 3:
[0] 7,8,9
[1] 7,8,9
[2] 7,8,9
Итак, для решения своей проблемы я создал функцию для рекурсивного поиска в List, извлечения похожих последовательностей и добавления их в отдельные списки. Мало того, что моя функция не работает, мой код очень длинный и сложный, и я чувствую, что должно быть решение, аналогичное тому, что я пытаюсь сделать. Будем признательны за любые предложения или советы, которые помогут мне двигаться в правильном направлении.
последний: 1,2,3
В таком случае я не понимаю вопроса. Что больше? 1,2,3 или 1,3,2? Почему?
порядок чисел не имеет значения, если последовательность содержит одинаковые числа.
всего пара небольших изменений изменит ваш пост в примере с реальными списками, содержащими реальные значения. Это мы могли скопировать и вставить и дать ему поработать. Это уменьшит наши усилия по оказанию вам помощи и увеличит скорость, с которой мы сможем это сделать. Вам бы помогли намного быстрее ...
1,2,3 похож на 3,2,1, войдут ли они в одну группу?
@MongZhu, да. 1,2,3 и 3,2,1 будут в одной группе. 1,2,4 и 3,2,1 будут разными группами
Разрешены отрицательные числа?
«Да. 1,2,3 и 3,2,1 будут в одной группе», как вы можете видеть, это жизненно важная информация. Вы должны включить это в свой пост. В противном случае вы получите ответы, которые не решат вашу проблему точно.





Я изо всех сил старался сократить необходимый код, чтобы выполнить то, что, как я предполагаю, вы хотите выполнить. Кстати, я помещаю полученные списки в список, который вы увидите:
этот следующий образец предназначен только для объявления вашего списка и ввода случайных значений:
List<List<int>> ContainerList = new List<List<int>>()
{
new List<int>()
{
0, 1, 2
},
new List<int>()
{
3, 4, 6
},
new List<int>()
{
0, 1, 2
},
new List<int>()
{
7, 8, 9
},
};
Теперь начинается полезная нагрузка:
List<List<List<int>>> result = new List<List<List<int>>>();
foreach (var cont in ContainerList)
result.Add(ContainerList.FindAll(x => x.SequenceEqual(cont)));
// the following erase duplicates
result = result.Distinct().ToList();
Итак, теперь вы можете получить свои подсписки как:
[0] [0] 012
[0] [1] 012
[1] [0] 346 ....
ОБЪЯСНЕНИЕ:
ContainerList.FindAll(x => x.SequenceEqual(cont))
В следующем фрагменте кода используется предикат: x - это значение в вашем списке.
Поскольку это список списка, ваш x будет списком
SequenceEqual означает, что функция Findall будет искать равенства по VALUE, а не по REFERENCE.
Затем мы удаляем дубликаты, потому что Findall на первом элементе ContainerList вернет список, содержащий все его дубликаты, соответствующие заданному параметру (то есть x).
Но по мере увеличения параметра (x) в списке. Вы собираетесь выполнить столько FindAll, сколько имеется значений одного и того же подмножества. Итак, в приведенном выше примере у вас будет 2 списка по 2 012;
Надеюсь, это понятно. Мой английский ужасен.
SequenceEqual, к сожалению, не распознает 1,2,3 и 3,2,1 как принадлежащие к одной группе.
Кроме того, Distinct недостаточно для удаления дубликатов из списка тройных
Это правда, я не видел, что требуется нарушение порядка, кстати, мы можем просто изменить последовательность, равную лямбде, которая проверяет первую и вторую последовательность в соответствии с предварительным условием. А для Distinct правильное использование Except должно работать нормально. И это намного проще, чем другие коды, поскольку для этого требуется всего несколько строк.
да, для Distinct нужен IEqualityComparer, который нужно либо реализовать, либо использовать с HashSet<T>. Как получить уникальные предметы, используя Except? мне интересно
Это код, который я бы использовал в приведенном вами примере.
static void Main(string[] args)
{
List<List<double>> lists = new List<List<double>>();
lists.Add(new List<double> { 1, 2, 3 });
lists.Add(new List<double> { 1, 2, 3 });
lists.Add(new List<double> { 1, 2, 3 });
lists.Add(new List<double> { 4, 5, 6 });
lists.Add(new List<double> { 4, 5, 6 });
lists.Add(new List<double> { 4, 5, 6 });
lists.Add(new List<double> { 7, 8, 9 });
lists.Add(new List<double> { 7, 8, 9 });
lists.Add(new List<double> { 7, 8, 9 });
lists.Add(new List<double> { 7, 8, 9 });
List<List<List<double>>> sortedLists = new List<List<List<double>>>();
for (int i = 0; i < lists.Count; i++)
{
bool found = false;
if (!(sortedLists.Count == 0))
{
for (int j = 0; j < sortedLists.Count; j++)
{
if (lists[i][0] == sortedLists[j][0][0] && lists[i][1] == sortedLists[j][0][1] && lists[i][2] == sortedLists[j][0][2])
{
found = true;
sortedLists[j].Add(lists[i]);
break;
}
}
}
if (!found)
{
sortedLists.Add(new List<List<double>> { lists[i] });
}
}
}
Единственное, что внутренний оператор if разработан специально для этого примера.
if (lists[i][0] == sortedLists[j][0][0] && lists[i][1] == sortedLists[j][0][1] && lists[i][2] == sortedLists[j][0][2])
Это необходимо изменить, если вы использовали что-то помимо трех двойных списков.
OP написал этот комментарий выше: «Да. 1,2,3 и 3,2,1 будут в одной группе», оператор if в вашем сообщении не может это объяснить. Эти числа распределяются по разным группам.
Я думаю, это сделает это за вас. Он работает с вашим требованием «не в порядке», то есть {1,2,3} равно {3,2,1} равно {2,3,1}.
static void Main(string[] args)
{
List<List<double>> list = new List<List<double>>()
{
new List<double>() { 1,2,3 },
new List<double>() { 4,5,6 },
new List<double>() { 7,8,9 },
new List<double>() { 2,3,1 },
new List<double>() { 5,6,4 },
new List<double>() { 8,9,7 },
new List<double>() { 3,1,2 },
new List<double>() { 6,4,5 },
new List<double>() { 9,7,8 },
};
// Pick a method, they both work
//var q2 = DictionaryMethod(list);
var q2 = LinqAggregateMethod(list);
foreach (var item in q2)
{
Console.WriteLine("List:");
foreach (var item2 in item)
Console.WriteLine($"\t{item2[0]}, {item2[1]}, {item2[2]}");
}
}
static bool ListsAreEqual(List<double> x, List<double> y)
{
foreach (var d in x.Distinct())
{
if (x.Count(i => i == d) != y.Count(i => i == d))
return false;
}
return true;
}
static IEnumerable<IEnumerable<List<double>>> LinqAggregateMethod(List<List<double>> list)
{
var q = list.Aggregate(new List<List<double>>() /* accumulator(ret) initial value */, (ret, dlist) =>
{
// ret = accumulator
// dlist = one of the List<double> from list
// If accumulator doesn't already contain dlist (or it's equal), add it
if (!ret.Any(dlistRet => ListsAreEqual(dlist, dlistRet)))
ret.Add(dlist);
return ret;
});
// At this point, q contains one 'version' of each list.
// foreach item in q, select all the items in list where the lists are equal
var q2 = q.Select(dlist => list.Where(item => ListsAreEqual(dlist, item)));
return q2;
}
static IEnumerable<IEnumerable<List<double>>> DictionaryMethod(List<List<double>> list)
{
var list2 = new Dictionary<List<double>, List<List<double>>>();
// Loop over each List<double> in list
foreach (var item in list)
{
// Does the dictionary have a key that is equal to this item?
var key = list2.Keys.FirstOrDefault(k => ListsAreEqual(k, item));
if (key == null)
{
// No key found, add it
list2[item] = new List<List<double>>();
}
else
{
// Key was found, add item to its value
list2[key].Add(item);
}
}
var q2 = new List<List<List<double>>>();
foreach (var key in list2.Keys)
{
var a = new List<List<double>>();
a.Add(key); // Add the key
a.AddRange(list2[key]); // Add the other lists
q2.Add(a);
}
return q2;
}
браво, единственное рабочее решение здесь :)
Вот еще один подход к этой проблеме. Я бы разделил это на 2 этапа.
// Sample input:
List<List<double>> lists = new List<List<double>>();
lists.Add(new List<double> { 1, 1, 3 });
lists.Add(new List<double> { 1, 3, 1 });
lists.Add(new List<double> { 3, 1, 1 });
lists.Add(new List<double> { 4, 5, 6 });
lists.Add(new List<double> { 4, 5, 6 });
lists.Add(new List<double> { 6, 5, 4 });
lists.Add(new List<double> { 7, 8, 9 });
lists.Add(new List<double> { 8, 7, 9 });
lists.Add(new List<double> { 9, 8, 7 });
1) Получите все уникальные списки из своей коллекции. Вы можете временно заказать их с помощью OrderBy. Это позволит провести сравнение с использованием Последовательность: равно:
List<List<double>> uniqueOrdered = new List<List<double>>();
foreach (var element in lists.Select(x => x.OrderBy(y => y).ToList()))
{
if (!uniqueOrdered.Any(x=> x.SequenceEqual(element)))
{
uniqueOrdered.Add(element);
}
}
2) Теперь у вас есть набор представителей для каждой из ваших групп. Просмотрите каждого представителя и получите все списки, которые соответствуют элементам в вашем представителе. Опять же, здесь вы можете временно их заказать для сравнения с SequenceEqual:
List<List<List<double>>> result = new List<List<List<double>>>();
foreach (var element in uniqueOrdered)
{
result.Add(lists.FindAll(x=> x.OrderBy(t=>t).SequenceEqual(element)));
}
Списки в результирующих группах сохранят исходный порядок!
«каждый список содержит 3 числа» <= Что это значит? Сто двадцать три - это одно число.
123. Вы действительно намеревались иметь 3 числа?1,2,3? Или это диапазон чисел от 100 до 999?