值之间的显式范围

值之间的显式范围

是否有任何干净的方法使用一些 bash 魔法来计算文本文件第二列的元素之间的范围? (我目前正在使用 Python 执行此操作)。

输入:文件1

A   1-5
A   17-19
B   1-5
B   4-6

预期输出:文件 2

A   1,2,3,4,5,17,18,19
B   1,2,3,4,5,6    

编辑@Anthon:为了累积元素,我正在使用类似的东西(然后使用 for 循环计算范围)

d_pos= {} 
for row in open('File.txt'): 
    x, y = [ value.strip() for value in row.split('\t')] 
    if x in d_pos:        
        d_pos[x].append(y)    
    else:        
        d_pos[x] = [y]

答案1

直接 bash,既然你问了(尽管注意我使用的是关联数组,需要 bash 4.0)。

诀窍在于大括号序列扩展表达式{x..y},对于整数 x ,任何 y 都会作为文本列表扩展到包含的值范围(即 [x,y])。我们也需要添加一个eval,因为大括号扩展发生在变量扩展之前。

declare -A data seen  # explicit associative arrays
while read col range; do
   data[$col]="${data[$col]} $(eval echo {${range/-/..}})"
done <<DATA
A   1-5
A   17-19
B   1-5
B   4-6
DATA

# dump array
#declare -p data

for ii in ${!data[@]}; do
    seen=();  datum=""
    # build list of unique values
    for dd in ${data[$ii]}; do
        (( ${seen[$dd]:-0} )) || datum="$datum $dd"
        let seen[$dd]++
    done

    datum=${datum# }     # drop leading space
    datum=${datum// /,}  # spaces to commas
    printf "%-4s %s\n" "$ii" "$datum"
done

序列扩展的一个变体是a{x..y}b在扩展的每个项前面添加“a”,并附加“b”:您可以使用它来附加一个“,”,并根据需要更改数据变量。序列扩展处理增量 1,如果 x > y,则处理 -1

您可能还需要对输出进行排序:迭代关联数组的键没有明确定义的顺序,并且您没有说明输入范围是否已预先排序(因此我没有使代码过于复杂)。

答案2

您的 Python 代码很接近,但例如无法处理项目 B 的 4 和 5 的重叠。

以下正确处理该问题,使用 aset()来防止重叠,使用 setdefault 来消除显式测试(如果键已存在于输入行中d_pos.split()输入行上),以减少对\t字符的依赖并消除显式测试.strip()

d_pos= {}
for row in open('File.txt'):
    x, y = [ value for value in row.split()]
    y1, y2 = map(int, y.split('-'))
    d_pos.setdefault(x, set()).update(range (y1, y2+1))
for x in sorted(d_pos):
    print '{}\t{}'.format(x, ','.join(map(str, d_pos[x])))

答案3

如果你可以使用perl

$ perl -MList::MoreUtils=uniq -anle '
    ($s,$e) = split "-", $F[1];
    push @{$h{$F[0]}}, $s..$e; 
    END {
        $" = ",";
        print "$_   @{[uniq@{$h{$_}}]}" for keys %h;
    }
' file
A   1,2,3,4,5,17,18,19
B   1,2,3,4,5,6

如果您不想使用List::MoreUtils,因为当它不在核心中时,您可以执行以下操作:

$ perl -anle '
    ($s,$e) = split "-", $F[1];
    push @{$h{$F[0]}}, $s..$e; 
    END {
        $" = ",";
        for $k (keys %h) {
            %u=();
            print "$k   @{[grep {!$u{$_}++} @{$h{$k}}]}";
        }
    }
' file

相关内容