Home > front end >  create sublist and sort in TCL
create sublist and sort in TCL

Time:02-16

I need to sort a sentence which is random in order. Each word in a sentence has an index value. e.g set sentence "will2 out4 Things1 work3" Output="Things will work out"

I tried using lsort -index 1 {{will 2} {out 4} {Things 1} {work 3}} This will give output as {Things 1} {will 2} {work 3} {out 4} I need help to create sublist and split the Input sentence to enable it for sorting.

CodePudding user response:

You have to first turn your words into a list of pairs that can be sorted with lsort, and then extract just the word part to re-join them into a sentence. It's a slight variation of what's known as the decorate-sort-undecorate operation.

#!/usr/bin/env tclsh

set sentence "will2 out4 Things1 work3"

# Turn sentence into a list of pairs of the words and numbers
set pairs [lmap word [split $sentence] {
    regexp {([[:alpha:]] )(\d )$} $word -> w n
    list $w $n    
}]

# Sort based on number much like you're doing in your question
set pairs [lsort -index 1 -integer $pairs]

# And re-join the first elements of the pairs
set sentence [join [lmap pair $pairs { lindex $pair 0 }]]

puts $sentence

If you do it all in one step without intermediate variables, it's known as a Schwartzian Transform (Though in this case I think readability suffers):

set sentence [join [lmap pair [lsort -index 1 -integer \
                                   [lmap word [split $sentence] {
                                       regexp {([[:alpha:]] )(\d )$} $word -> w n
                                       list $w $n
                                   }]] { lindex $pair 0 }]]

CodePudding user response:

If your sentence has nine or fewer words, you can reverse each word with lmap, sort on the reversed words, then un-reverse.

set sentence "will2 out4 Things1 work3"
                                    
set ecnetnes [lmap word $sentence {string reverse $word}]

set sorted_ecnetnes [lsort $ecnetnes]

set sorted_sentence [lmap word $sorted_ecnetnes {string range [string reverse $word] 0 end-1}]

--> Things will work out

CodePudding user response:

The parsing of a word into its text and index part can be done with regexp (\D matches a non-digit, and \d matches a digit):

regexp {(\D )(\d )} $word -> text index

You then can either use this by transforming the list or by making auxiliary lists for sorting purposes.

In performance terms, these two techniques are incredibly similar on your sample input (assuming the obvious conversions to procedures) with less than 1% difference in timing. Either they're doing the same amount of work really, or they're making similar mistakes in terms of excess effort.

Sorting by list transformation

set input "will2 out4 Things1 work3"
set prepared [lmap word $input {
    lrange [regexp -inline {(\D )(\d )} $word] 1 end
}]
set sorted [lsort -integer -index 1 $prepared]
set output [lmap pair $sorted {
    lindex $pair 0
}]
# Converting that into a one-liner is left as an obvious exercise

Sorting with auxiliary lists

This uses the -indices option to lsort, which tells you what order the values in the list would be sorted into; this is great if you want to apply that order to something else.

set input "will2 out4 Things1 work3"
set texts [set values {}]
foreach word $input {
    regexp {(\D )(\d )} $word -> t v
    lappend texts $t
    lappend values $v
}
set sorted [lsort -indices -integer $values]
set output [lmap idx $sorted {
    lindex $texts $idx
}]
  • Related