我有一个包含值的列表:
set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
我想为后续相同的值添加一个计数器,如下所示:
numbered [ 101.1 101.2 101.3 102.1 102.2 103 104 105.1 105.2 105.3 106]
到目前为止,我已尝试过以下内容:
set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
set numbered [list ]
set previous [lindex $unnumbered 0]
set subcounter 1
foreach current $unnumbered {
if { $current eq $previous } {
lappend numbered $current.$counter
set previous $current
incr subcounter
} else {
lappend numbered $current
set previous $current
set subcounter 1
}
}
结果几乎是我所需要的。
101.1 101.2 101.3 102 102.1 103 104 105 105.1 105.2 106
对于除第一个值之外的所有值,计数器开始计数到很晚。第一个102说“.1”
我该如何解决这个问题?
答案 0 :(得分:0)
问题是您的代码在将数字添加到numbered
时没有足够的信息。首先获取信息,然后应用它。
首先,创建一个列表,其中每个项目都是一个列表,其中包含$unnumbered
中的一个唯一数字和$unnumbered
中出现该数字的索引:
lmap n [lsort -unique $unnumbered] {
list $n [lsearch -all $unnumbered $n]
}
# => {101 {0 1 2}} {102 {3 4}} {103 5} {104 6} {105 {7 8 9}} {106 10}
对于每个项目,将项目拆分为n
=数字和indices
=指数。检查你有多少指数。对于多个索引,请添加如下的枚举数字:
set i 0
foreach index $indices {
lappend numbered $n.[incr i]
}
对于单个索引,只需添加数字:
lappend numbered $n
整个程序看起来像这样:
set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
set numbered [list]
foreach item [lmap n [lsort -unique $unnumbered] {
list $n [lsearch -all $unnumbered $n]
}] {
lassign $item n indices
if {[llength $indices] > 1} {
set i 0
foreach index $indices {
lappend numbered $n.[incr i]
}
} else {
lappend numbered $n
}
}
文档: > (operator), foreach, if, incr, lappend, lassign, list, llength, lmap (for Tcl 8.5), lmap, lsearch, lsort, set
如果您没有lmap
,请参阅上面的链接。如果您没有lassign
,请使用
foreach {n indices} $item break
代替。
ETA 如果可以放宽“无单号码索引”要求,可以这样做:
set previous {}
lmap num $unnumbered {
if {$num ne $previous} {
set i 0
}
set previous $num
format %d.%d $num [incr i]
}
另一种变体。这与杰里的第二个建议非常相似,但在我提交这个之前我没有看到这个,诚实。这假设$unnumbered
中的任何元素都不是空字符串。
set numbered [list]
set rest [lassign $unnumbered current next]
set i 0
while 1 {
if {$current eq $next} {
lappend numbered $current.[incr i]
} else {
if {$i > 0} {
lappend numbered $current.[incr i]
set i 0
} else {
lappend numbered $current
}
set current $next
}
if {$next eq {}} break
set rest [lassign $rest next]
}
答案 1 :(得分:0)
另一种方法:保持一个字数以保持你目前所看到的数量
set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
set count [dict create]
set numbered {}
foreach num $unnumbered {
dict incr count $num
lappend numbered "$num.[dict get $count $num]"
}
puts $numbered
101.1 101.2 101.3 102.1 102.2 103.1 104.1 105.1 105.2 105.3 106.1
使用数组有点简单:利用incr
返回新计数的事实
set numbered {}
array set count {}
foreach num $unnumbered {lappend numbered "$num.[incr count($num)]"}
好的,我错过了单例条目不应该有后缀的要求。有这个,但它可能重新排序初始列表:
set count [dict create]
foreach num $unnumbered {dict incr count $num}
set numbered {}
foreach num [dict keys $count] {
set c [dict get $count $num]
if {$c == 1} {
lappend numbered $num
} else {
for {set i 1} {$i <= $c} {incr i} {
lappend numbered "$num.$i"
}
}
}
puts $numbered
101.1 101.2 101.3 102.1 102.2 103 104 105.1 105.2 105.3 106
或者,这维持原始顺序
set count [dict create]
foreach num $unnumbered {dict incr count $num}
foreach key [dict keys $count] {
if {[dict get $count $key] == 1} {
set count [dict remove $count $key]
}
}
set numbered {}
foreach num [lreverse $unnumbered] {
if {![dict exists $count $num]} {
lappend numbered $num
} else {
lappend numbered "$num.[dict get $count $num]"
dict incr count $num -1
}
}
set numbered [lreverse $numbered]
puts $numbered
101.1 101.2 101.3 102.1 102.2 103 104 105.1 105.2 105.3 106
答案 2 :(得分:0)
一个O(n)解决方案(单循环),我认为它看起来更像你最初想要实现的方式:
set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
set numbered [list]
set previous ""
set subcounter 1
foreach current $unnumbered {
if {$previous == ""} {
# First, do nothing except set $current to $previous later below
} elseif {$previous == $current} {
lappend numbered $previous.$subcounter
incr subcounter
} else {
if {$subcounter > 1} {
lappend numbered $previous.$subcounter
} else {
lappend numbered $previous
}
set subcounter 1
}
set previous $current
}
if {$subcounter > 1} {
lappend numbered $current.$subcounter
} else {
lappend numbered $current
}
循环基本上会在numbered
列表后面添加一个数字,因此最后一个数字需要最后一个if
。当然,这只有在您知道unnumbered
已排序时才有效。
$previous
,因此您可以从列表的下一个元素开始循环,最后一个元素在最后一个元素之后循环(请注意,如果lindex
提供了一个空白,我们会得到一个空白范围索引,这使事情变得更容易。)
set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
set numbered [list]
set previous [lindex $unnumbered 0]
set subcounter 1
for {set i 1} {$i <= [llength $unnumbered]} {incr i} {
if {$previous == [lindex $unnumbered $i]} {
lappend numbered $previous.$subcounter
incr subcounter
} else {
if {$subcounter > 1} {
lappend numbered $previous.$subcounter
} else {
lappend numbered $previous
}
set subcounter 1
}
set previous [lindex $unnumbered $i]
}