Как отсортировать список с дублирующими ключами?
У меня есть набор элементов/ключей, которые я читаю из двух разных файлов конфигурации. Таким образом, ключи могут быть одинаковыми, но с разными значениями, связанными с каждым из них.
Я хочу перечислить их в отсортированном порядке. Что я могу сделать? Я пробовал с классом SortedList
, но он не позволяет дублировать ключи.
Как я могу это сделать?
Например, скажем, у меня есть 3 элемента с ключами 1,2,3. Затем я получаю еще один элемент, имеющий ключ 2 (но другое значение). Затем я хочу, чтобы новый ключ вставлен после существующего ключа 2, но до 3. Если я найду элемент с ключом 2, он должен идти после последнего добавленного ключа 2.
Обратите внимание, что я использую .NET 2.0
Ответы
Ответ 1
Я предпочитаю использовать LINQ для этого типа вещей:
using System.Linq;
...
var mySortedList = myList.Orderby(l => l.Key)
.ThenBy(l => l.Value);
foreach (var sortedItem in mySortedList) {
//You'd see each item in the order you specified in the loop here.
}
Примечание. Для этого вы должны использовать .NET 3.5 или новее.
Ответ 2
вам нужна функция Sort с пользовательским IComparer. Теперь вы используете icomparer по умолчанию при использовании сортировки. это проверит значение поля.
Когда вы создаете пользовательский IComparer (вы делаете это в своем классе, реализуя Icomparable интерфейс). что он делает: ваш объект проверяет себя на каждый другой объект в отсортированном вами списке.
это выполняется функцией. (не беспокойтесь, VS будет реализовывать его при обращении к вашему интерфейсу
public class ThisObjectCLass : IComparable{
public int CompareTo(object obj) {
ThisObjectCLass something = obj as ThisObjectCLass ;
if (something!= null)
if(this.key.CompareTo(object.key) == 0){
//then:
if .....
}
else if(this.value "is more important then(use some logic here)" something.value){
return 1
}
else return -1
else
throw new ArgumentException("I am a dumb little rabid, trying to compare different base classes");
}
}
прочитайте приведенные выше ссылки для получения более подробной информации.
Я знаю, что у меня были некоторые проблемы, которые понимали это сами в начале, так что для любой дополнительной помощи добавьте комментарий, и я уточню
Ответ 3
Я сделал это, создав SortedList<int, List<string>>
. Всякий раз, когда я нахожу дубликат, я просто вставляю значение в существующий список, связанный с ключом, уже присутствующим в объекте SortedList. Таким образом, у меня может быть список значений для определенного ключа.
Ответ 4
Используйте свой собственный класс сравнения!
Если ваши ключи в отсортированном списке являются целыми числами, вы можете использовать, например, этот компаратор:
public class DegreeComparer : IComparer<int>
{
#region IComparer<int> Members
public int Compare(int x, int y)
{
if (x < y)
return -1;
else
return 1;
}
#endregion
}
Чтобы создать новый SortedList с ключами int и строковыми значениями, используйте:
var mySortedList = new SortedList<int, string>(new DegreeComparer());
Ответ 5
Если вы не заботитесь о последовательности элементов с равными ключами, добавьте все в список, а затем отсортируйте по клавише:
static void Main(string[] args)
{
List<KeyValuePair<int, MyClass>> sortedList =
new List<KeyValuePair<int, MyClass>>() {
new KeyValuePair<int, MyClass>(4, new MyClass("four")),
new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
new KeyValuePair<int, MyClass>(5, new MyClass("five")),
new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
new KeyValuePair<int, MyClass>(7, new MyClass("seven-b"))
};
sortedList.Sort(Compare);
}
static int Compare(KeyValuePair<int, MyClass> a, KeyValuePair<int, MyClass> b)
{
return a.Key.CompareTo(b.Key);
}
Если вы действительно хотите, чтобы элементы, вставленные позже, были после вставленных ранее, сортируйте их по мере их вставки:
class Sorter : IComparer<KeyValuePair<int, MyClass>>
{
static void Main(string[] args)
{
List<KeyValuePair<int, MyClass>> sortedList = new List<KeyValuePair<int, MyClass>>();
Sorter sorter = new Sorter();
foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] {
new KeyValuePair<int, MyClass>(4, new MyClass("four")),
new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
new KeyValuePair<int, MyClass>(5, new MyClass("five")),
new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
new KeyValuePair<int, MyClass>(4, new MyClass("four-c")),
new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) })
{
sorter.Insert(sortedList, kv);
}
for (int i = 0; i < sortedList.Count; i++)
{
Console.WriteLine(sortedList[i].ToString());
}
}
void Insert(List<KeyValuePair<int, MyClass>> sortedList, KeyValuePair<int, MyClass> newItem)
{
int newIndex = sortedList.BinarySearch(newItem, this);
if (newIndex < 0)
sortedList.Insert(~newIndex, newItem);
else
{
while (newIndex < sortedList.Count && (sortedList[newIndex].Key == newItem.Key))
newIndex++;
sortedList.Insert(newIndex, newItem);
}
}
#region IComparer<KeyValuePair<int,MyClass>> Members
public int Compare(KeyValuePair<int, MyClass> x, KeyValuePair<int, MyClass> y)
{
return x.Key.CompareTo(y.Key);
}
#endregion
}
Или у вас может быть отсортированный список списков:
static void Main(string[] args)
{
SortedDictionary<int, List<MyClass>> sortedList = new SortedDictionary<int,List<MyClass>>();
foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] {
new KeyValuePair<int, MyClass>(4, new MyClass("four")),
new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
new KeyValuePair<int, MyClass>(5, new MyClass("five")),
new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
new KeyValuePair<int, MyClass>(4, new MyClass("four-c")),
new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) })
{
List<MyClass> bucket;
if (!sortedList.TryGetValue(kv.Key, out bucket))
sortedList[kv.Key] = bucket = new List<MyClass>();
bucket.Add(kv.Value);
}
foreach(KeyValuePair<int, List<MyClass>> kv in sortedList)
{
for (int i = 0; i < kv.Value.Count; i++ )
Console.WriteLine(kv.Value[i].ToString());
}
}
Я не уверен, что вы можете использовать инициализаторы List в .NET 2.0, как это было в первом примере выше, но я уверен, что вы знаете, как заполнить список данными.
Ответ 6
.NET не имеет огромной поддержки для стабильных сортировок (что означает, что эквивалентные элементы сохраняют свой относительный порядок при сортировке). Однако вы можете написать свою стабильную сортировку-вставку, используя List.BinarySearch
и пользовательский IComparer<T>
(который возвращает -1, если ключ меньше или равен цели, а +1, если больше).
Обратите внимание, что List.Sort
не является стабильным, поэтому вам придется либо написать свою собственную стабильную процедуру быстрой сортировки, либо просто использовать сортировку вставки, чтобы изначально заполнить коллекцию.
Ответ 7
Как насчет этого
SortedList<string, List<string>> sl = new SortedList<string, List<string>>();
List<string> x = new List<string>();
x.Add("5");
x.Add("1");
x.Add("5");
// use this to load
foreach (string z in x)
{
if (!sl.TryGetValue(z, out x))
{
sl.Add(z, new List<string>());
}
sl[z].Add("F"+z);
}
// use this to print
foreach (string key in sl.Keys)
{
Console.Write("key=" + key + Environment.NewLine);
foreach (string item in sl[key])
{
Console.WriteLine(item);
}
}
Ответ 8
Вы рассматривали класс NameValueCollection, так как он позволяет хранить несколько значений на ключ? вы можете, например, иметь следующее:
NameValueCollection nvc = new NameValueCollection();
nvc.Add("1", "one");
nvc.Add("2", "two");
nvc.Add("3", "three");
nvc.Add("2", "another value for two");
nvc.Add("1", "one bis");
а затем для получения значений, которые вы могли бы иметь:
for (int i = 0; i < nvc.Count; i++)
{
if (nvc.GetValues(i).Length > 1)
{
for (int x = 0; x < nvc.GetValues(i).Length; x++)
{
Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i).GetValue(x));
}
}
else
{
Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i)[0]);
}
}
которые выдают результат:
'1' = 'one'
'1' = 'one bis'
'2' = 'two'
'2' = 'другое значение для двух'
'3' = 'three'
Ответ 9
В .NET 2.0 вы можете написать:
List<KeyValuePair<string, string>> keyValueList = new List<KeyValuePair<string, string>>();
// Simulate your list of key/value pair which key could be duplicate
keyValueList.Add(new KeyValuePair<string,string>("1","One"));
keyValueList.Add(new KeyValuePair<string,string>("2","Two"));
keyValueList.Add(new KeyValuePair<string,string>("3","Three"));
// Here an entry with duplicate key and new value
keyValueList.Add(new KeyValuePair<string, string>("2", "NEW TWO"));
// Your final sorted list with one unique key
SortedList<string, string> sortedList = new SortedList<string, string>();
foreach (KeyValuePair<string, string> s in keyValueList)
{
// Use the Indexer instead of Add method
sortedList[s.Key] = s.Value;
}
Выход:
[1, One]
[2, NEW TWO]
[3, Three]
Ответ 10
У меня была аналогичная проблема, когда я разрабатывал игру, похожую на концепцию шахматной игры, в которой у вас есть компьютер. Мне нужно было иметь возможность сделать несколько кусков, чтобы сделать ход, и поэтому мне нужно было иметь несколько штатов Советов. Каждому члену правления необходимо было занять место на основе позиции частей. Для аргументации и простоты, скажем, моя игра была Noughts and Crosses, и я был Noughts, а Computer был Crosses. Если состояние платы показывало 3 строки Noughts, то это лучшее состояние для меня, если оно показывает 3 в ряду крестов, то это худшее состояние для меня и лучше всего подходит для компьютера. Во время игры есть другие состояния, которые более удобны для одного или другого, и кроме того, есть многословные состояния, которые приводят к рисованию, так как я могу его ранжировать, когда есть равные оценки ранга. Это то, что я придумал (извинись заранее, если вы не программист VB).
Мой класс сравнения:
Class ByRankScoreComparer
Implements IComparer(Of BoardState)
Public Function Compare(ByVal bs1 As BoardState, ByVal bs2 As BoardState) As Integer Implements IComparer(Of BoardState).Compare
Dim result As Integer = bs2.RankScore.CompareTo(bs1.RankScore) 'DESCENDING order
If result = 0 Then
result = bs1.Index.CompareTo(bs2.Index)
End If
Return result
End Function
End Class
Мои объявления:
Dim boardStates As SortedSet(Of BoardState)(New ByRankScoreComparer)
Реализация моего правления:
Class BoardState
Private Shared BoardStateIndex As Integer = 0
Public ReadOnly Index As Integer
...
Public Sub New ()
BoardStateIndex += 1
Index = BoardStateIndex
End Sub
...
End Class
Как вы можете видеть, RankScores поддерживаются в порядке убывания, и любые 2 состояния, имеющие одинаковый ранговый балл, более позднее состояние уходит в нижнее положение, так как оно всегда будет иметь больший назначенный Индекс и, таким образом, это позволяет дублировать. Я также могу безопасно вызвать boardStates.Remove(myCurrentBoardState), который также использует компаратор, и компаратор должен вернуть значение 0, чтобы найти объект, подлежащий удалению.