For that to work in Tcl 8.0, you need to use arrays and you need to do additional work because incr doesn't auto-initialise variables.
foreach item $list {
if {![info exists counter($item)]} {
set counter($item) 0
}
incr counter($item)
}
Unfortunately, info exists is not optimised prior to Tcl 8.5 so this might be faster:
foreach item $list {
if {[catch {incr counter($item)}]} {
set counter($item) 1
}
}
I've not measured, but I'd imagine the best option will depend on whether throwing an error and catching it (definitely expensive!) is common or not, which will depend on your input data and the number of duplicates in it.
Printing out:
foreach item [array names counter] {
puts [format "%6s %-3d" $item $counter($item)]
}
The order that things are printed out is undefined. (Technically it is deterministic and could be defined, but the algorithm is... complicated and nobody wants to trace through it. Pretend it is random.) If you want to sort the elements, run the list out of array names through lsort; that's what parray has always done!
Printing in the first-occurrence order would probably be best done by also keeping a separate list of items in the order that you initialise their array cells.
foreach item $list {
if {![info exists counter($item)]} {
set counter($item) 0
lappend firstOccurrences $item
}
incr counter($item)
}
foreach item $firstOccurrences {
puts [format "%6s %-3d" $item $counter($item)]
}
You could also do this:
foreach item $list {
append counter($item) "."
# Check if result of append is of length 1 here if you want to build an occurrence list
}
foreach item [array names counter] {
puts [format "%6s %-3d" $item [string length $counter($item)]]
}
but the amount of memory used will be quite annoying for larger lists of items.
You could use lappend/llength instead, but you'd still have the memory problem.