Yogesh Agrawal
Yogesh Agrawal

Reputation: 1

sorting of 2D list in tcl

Though I read many options in lsort to do this, but in my case it is not working. I have a list of floating numbers like below

{898.465 348.700} {898.465 1511.400} {477.130 348.700} {898.465 730.200} {477.130 1511.400} {898.465 1121.400} {477.130 730.200} {477.130 1121.400}

I want to sort it by second index, so that I can get min and max value of 2nd index. I tried below things and got few outputs but nothing is of any help.

lsort -index end-1  -decreasing  `$bbox_upper`
{477.130 348.700} {477.130 1511.400} {477.130 730.200} {477.130 1121.400} {898.465 348.700} {898.465 1511.400} {898.465 730.200} {898.465 1121.400}

lsort -index 1 -real $bbox_upper
{898.465 1121.400} {477.130 1121.400} {898.465 1511.400} {477.130 1511.400} {898.465 348.700} {477.130 348.700} {898.465 730.200} {477.130 730.200}

If you see in above outputs, the 2nd index 1511.400 is the biggest one, so it should come in the first sublist, but it is not coming

Can you please help.

Upvotes: 0

Views: 2850

Answers (1)

Donal Fellows
Donal Fellows

Reputation: 137587

What exact version of Tcl are you using (using info patchlevel to find out)? When I try it with 8.6.1, it works fine as long as I use all the options I need (and I'd also expect it to be fine with any 8.5-based or 8.4-based Tcl):

% info patchlevel
8.6.1
% lsort -index 1 -decreasing -real $bbox_upper
{898.465 1511.400} {477.130 1511.400} {898.465 1121.400} {477.130 1121.400} {898.465 730.200} {477.130 730.200} {898.465 348.700} {477.130 348.700}

I wouldn't use the index end-1 unless you mean “last but one”. For numeric sorting, the -real option is right; -dictionary might also work, but it's using a very different string-based algorithm (that has good results with values shown to users in things like lists of filenames in file chooser dialogs) and wouldn't be efficient for true floating point data.


If you need a more complex sort, such as using the first value of each pair as a tiebreak when the second value is the same, you've got a few options.

Secondary keys are usually best handled via sorting the data twice. It's not the most efficient technique, but it's easy to get right and Tcl's lsort uses a stable sorting algorithm.

lsort -index 1 -decreasing -real [lsort -index 0 -decreasing -real $bbox_upper]

Alternatively, you can use the -command option to delegate the ordering decision to some code you provide:

proc sorter {aValue bValue} {
    foreach {ax ay} $aValue break
    foreach {bx by} $bValue break
    set diff [expr {$ay - $by}]
    if {$diff == 0} {
        set diff [expr {$ax - $bx}]
    }
    return [expr {$diff < 0 ? 1 : $diff > 0 ? -1 : 0}]
}
lsort -command sorter $bbox_upper

From 8.5 onwards, you can do this without introducing a helper procedure:

lsort -command {apply {{aValue bValue} {
    lassign $aValue ax ay
    lassign $bValue bx by
    set diff [expr {$ay - $by}]
    if {$diff == 0} {
        set diff [expr {$ax - $bx}]
    }
    return [expr {$diff < 0 ? 1 : $diff > 0 ? -1 : 0}]
}}} $bbox_upper

You're advised that it is faster to use a procedure, and even faster still (at least in this case) a double sort.

Upvotes: 4

Related Questions