У меня есть словарь в tcl, где значением является список элементов. Например, рассмотрим словарь, приведенный ниже:
1: {10,20,30}
2: {10,25,45}
3: {10,5,15}
4: {2,12,36}
5: {7,16,9}
где 1,2,3,4,5 — ключи, а значения представлены в фигурных скобках. Я хочу отсортировать словарь на основе первого элемента значения списка {index 1 0}. Если 2 ключа имеют одинаковое значение для {index 1 0}, я должен отсортировать по второму элементу значения списка, {index 1 1}. Наконец, я должен вернуть ключи в отсортированном порядке. В настоящее время я использую lsort для сортировки содержимого. Но я не могу сортировать, если два ключа имеют одинаковое значение {index 1 0}. Я использую tcl 8.6. В настоящее время я использую следующий код:
set sorted [lsort -stride 2 -integer -index {1 0} $dict_name]
set get_keys [dict keys $sorted ]
puts $get_keys
Но приведенный выше код не сортирует содержимое со вторым элементом списка, если оба имеют одинаковое значение для индекса {1 0}. Ожидаемый результат:
4: {2,12,36}
5: {7,16,9}
3: {10,5,15}
1: {10,20,30}
2: {10,25,45}
Ключ 4 имеет наименьшее значение индекса 0, за ним следует ключ 5. Поскольку ключи 1, 2 и 3 имеют одинаковое значение индекса 0, они сортируются на основе значений индекса 1.
Если вы пишете функцию, которая сравнивает два списка поэлементно, ее можно использовать с параметрами lsort
, -stride
и -index
для сортировки по желанию, передавая значения dict в эту функцию сравнения.
#!/usr/bin/env tclsh
# Compare two values of integer lists element by element
proc sortIntLists {a b} {
set lenA [llength $a]
set lenB [llength $b]
set len [::tcl::mathfunc::min $lenA $lenB]
for {set n 0} {$n < $len} {incr n} {
set diff [expr {[lindex $a $n] - [lindex $b $n]}]
if {$diff != 0} {
return $diff
}
}
# Every pair of elements of the lists looked at has been equal
# now if the lists are different lengths the shorter one is smaller
if {$lenA == $lenB} {
return 0
} elseif {$lenA < $lenB} {
return -1
} else {
return 1
}
}
set myDict {
1 {10 20 30}
2 {10 25 45}
3 {10 5 15}
4 {2 12 36}
5 {7 16 9}
}
set mySortedDict [lsort -stride 2 -index 1 -command sortIntLists $myDict]
dict for {k v} $mySortedDict {
puts "$k: $v"
}
выходы
4: 2 12 36
5: 7 16 9
3: 10 5 15
1: 10 20 30
2: 10 25 45
@SouravDas Поигравшись с различными комбинациями вариантов lsort
, да, я нашел способ. Смотрите пересмотренный ответ. Теперь я чувствую себя глупо из-за того, что не исследовал этот подход более оригинально.
Я не знаю, как вам благодарить, но это именно то, что я хотел.
Tcl использует стабильный алгоритм сортировки. Чтобы устранить дубликаты в первичном ключе с помощью вторичного ключа, сначала выполните сортировку по вторичному ключу, а затем отсортируйте результат по первичному ключу:
set data {
1 {10 20 30}
2 {10 25 45}
3 {10 5 15}
4 {2 12 36}
5 {7 16 9}
}
set temp [lsort -stride 2 -integer -index {1 1} $data]
set final [lsort -stride 2 -integer -index {1 0} $temp]
puts [dict keys $final]
Результат: 4 5 3 1 2
Вы можете расширить это на любое количество ключей. Просто начните сортировку по наименее значимому ключу и продвигайтесь к наиболее значимому ключу.
@Scelte Брон. Это тоже выглядит просто и круто.
Спасибо, @Шон. Кажется, это работает нормально, но, как вы упомянули, есть ли способ заставить его работать с параметром шага и команды lsort?