Home > Software design >  sorting a tcl array according to a value in the key
sorting a tcl array according to a value in the key

Time:06-29

I noticed a lot of examples here how to sort an array with 1 value per each key, question is: how can I sort an array with an elaborated value?
e.g.

set a(one) "some sort of text and num 234"
set a(two) "same format of text with 123"
set a(three) "same format of text with 12.3"

How can I sort according to the number at the end of the value?
Currently I'm pushing the text to a file and sorts it using sort, but this is ugly and cumbersome.
My end goal is to print the sorted array into a file.

CodePudding user response:

My approach is to turn the array into a list, then sort that list:

set a(one) "some sort of text and num 234"
set a(two) "same format of text with 123"
set a(three) "same format of text with 72.3"

parray a

# Convert array into list of {number key value} then sort
set aList {}
foreach {key value} [array get a] {
    lappend aList [list $key $value]
}
# aList now is {
#     {one "some sort of text and num 234"}
#     {two "same format of text with 123"}
#     {three "same format of text with 72.3"}
# }

# Sort and print
# The -index {1 end} tells lsort to first pick index 1 (the value)
# then pick index "end" (last index) of the value, which is the
# number. Finally, the -real flag tells lsort to sort by real numbers,
# not by text value.
puts "---"
foreach sublist [lsort -index {1 end} -real $aList] {
    lassign $sublist key value
    puts "$key: $value"
}

Output:

a(one)   = some sort of text and num 234
a(three) = same format of text with 72.3
a(two)   = same format of text with 123
---
three: same format of text with 72.3
two: same format of text with 123
one: some sort of text and num 234

CodePudding user response:

Since the -stride flag of lsort is useless here, solved it with a list of lists:

 foreach {k v} [array get paths] {                                                                                                                               
   lappend xx [concat $k $v]                                                                                                                         
 } 
 lsort  -index 11 $xx 

Can reconstruct the array later on (I remained with the list of lists)

CodePudding user response:

We can store the array in a dict, and use lsort on that:

proc last_word_sort {a b} {
    set last_a [lindex [split $a] end]
    set last_b [lindex [split $b] end]
    if {$last_a < $last_b} then {return -1}
    if {$last_a > $last_b} then {return  1}
    return 0
} 

set a(one) "some sort of text and num 234"
set a(two) "same format of text with 123"
set a(three) "same format of text with 12.3"

set b [dict create {*}[array get a]]

foreach {key value} [lsort -stride 2 -index 1 -command last_word_sort $b] {
    puts "$key => $value"
}
three => same format of text with 12.3
two => same format of text with 123
one => some sort of text and num 234
  • Related