2

I have a dict which looks like this:

Unsorted:

12 {12 489} 29 {89 12} 27 {301 302} 26 {489 329} 8  {89 302} 55 {44 301}

I would like to sort it like this:

55 {44 301} 27 {301 302} 8  {89 302} 29 {89 12} 12 {12 489} 26 {489 329}

As you can see, most of the time the second key value of the preceding entry is identical to the first key entry of the following entry. ( 12 and 489 in the last two entries)

This although is no requirement. The 302 of the second and third entry also fullfills the requirement of a "chain" as it exists in both the second and the third entry.

The only thing I want to do is sorting these entries in such a way, that the values in braces form a uninterupted chain.

It does not matter if the result looks like in the example or if it is mirrored.

From TCL 8.6 on I could do something similar to Sort Tcl dict by value using stride. But I'm stuck with this (Tcl8.5.9) version. What is the easiest way to this?

Community
  • 1
  • 1
Lumpi
  • 2,697
  • 5
  • 38
  • 47

1 Answers1

1

I don't know if this is the easiest way:

set x [dict create 12 {12 489} 29 {89 12} 27 {301 302} 26 {489 329} 8 {89 302} 55 {44 301}]

# transform the dict into a list of lists
dict for {k v} $x {lappend unsorted [list $k $v]}
lappend sorted [lindex $unsorted 0]
set unsorted [lrange $unsorted 1 end]

# keep going until there's nothing more to add to the sorted list
while {[llength $unsorted] != 0} {
    set changed false

    for {set idx 0} {$idx < [llength $unsorted]} {incr idx} {
        set elem [lindex $unsorted $idx]
        lassign [lindex $elem end] a b

        set head [lindex $sorted 0 end]
        set tail [lindex $sorted end end]

        if {$a in $head || $b in $head} {
            set sorted [linsert $sorted 0 $elem]
            set changed true
        } elseif {$a in $tail || $b in $tail} {
            lappend sorted $elem
            set changed true
        }

        if {$changed} {
            set unsorted [lreplace $unsorted $idx $idx]
            break
        }
    }

    # avoid infinite loop if the unsorted list is not empty, but
    # contains nothing to add to the sorted list
    if {! $changed} break
}

foreach elem $sorted {dict set y {*}$elem}

puts "Unsorted: $x"
puts "Sorted:   $y"
Unsorted: 12 {12 489} 29 {89 12} 27 {301 302} 26 {489 329} 8 {89 302} 55 {44 301}
Sorted:   55 {44 301} 27 {301 302} 8 {89 302} 29 {89 12} 12 {12 489} 26 {489 329}
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • You headed in the same direction as I did, but you were faster! Seems to work. One question: I have seen things like {wildcard} in TCL code a couple of times now, but I do not understand what it means. {wildcard} is also fairly hard to google if you want reasonebable results.... Is it in the manual somewhere ? – Lumpi Feb 10 '14 at 17:54
  • 1
    This is rule #5 in the Tcl syntax: http://tcl.tk/man/tcl8.5/TclCmd/Tcl.htm -- Normally when you say `cmd $foo`, the command receives exactly 1 argument, even if it is a list -- when you say `cmd {*}$foo` the argument is exanded into its elements and the command receives however many elements there are in $foo. – glenn jackman Feb 10 '14 at 18:09
  • The code fails with this example: set p [ dict create 12 {12 489} 29 {89 12} 27 {301 302} 26 {489 329} 8 {89 302} 55 {44 301} ] I'll be back if i find the cause... – Lumpi Feb 10 '14 at 19:17
  • In this case you have 3 separate "chains": `29 {89 12} 12 {12 489} 26 {489 329}`, `55 {44 301} 27 {301 302}` and `8 {89 302}` -- what should you do in this case? Granted, my code does not find all the chains, and arbitrarily starting with the first element may not even find the longest chain (although it happens to do so in this case). What are your requirements? – glenn jackman Feb 10 '14 at 19:25
  • Isn't this some kind of graph construction problem? Or a specialization of one? There _must_ be some general algorithms for this sort of thing, but I'm too tired to remember the details. – Donal Fellows Feb 10 '14 at 20:01
  • Well, I dont see it as three seperate chains, as you can glue the third chain in front of the first connecting via the 89. And the second chain can be attached to the end of last chain via the 302. So it actually is one chain if it is shuffeled around "correctly". I started by finding the END or START element at first, a value which only occours once. From that part on i try to attach the remaining ones. I post some code (not final yet) tomorrow. – Lumpi Feb 10 '14 at 20:02
  • Your comment does not agree with the requirements in the question: "the **second key** value of the preceding entry is identical to the **first key** entry of the following entry" – glenn jackman Feb 10 '14 at 20:37
  • Your right, it was not the perfect example. I revised the question and hope it's clearer now. – Lumpi Feb 10 '14 at 21:40
  • I know that this kind of comment is not appreciated here but as it is the last one regarding this question: May I bye you a beer ? Thank you! – Lumpi Feb 11 '14 at 08:22