Tạo danh sách các từ theo số nhị phân


12

Tôi có một ma trận giống như sau:

Đầu vào :

A   B   C   D   E   F   G   H   I 
0   0   0   0   1   0   0   0   1
0   0   0   1   0   0   0   0   0  
0   0   0   1   0   0   0   0   0  
1   0   0   0   0   0   0   0   0  
1   0   1   0   0   0   1   0   0  
1   0   0   1   0   0   0   1   0  
1   0   0   0   1   1   1   0   0  

Và tôi muốn trích xuất cho mỗi hàng danh sách các chữ cái tương ứng với giá trị 1.

Đầu ra :

E,I 
D
D
A
A,C,G  
A,D,H  
A,E,F,G  

Tôi đã cố tách tiêu đề và nối các từ với số nhưng tôi đã thất bại.

Câu trả lời:


12

Trong awk:

NR == 1 { for(column=1; column <= NF; column++) values[column]=$column; }
NR > 1 { output=""
        for(column=1; column <= NF; column++)
                if($column) output=output ? output "," values[column] : values[column]
        print output }

6
cũng có thể sử dụngNR == 1 { split($0,values) }
Sundeep

Đó là bỏ qua dòng thứ 2. Cân nhắc đặt một dòng nextở cuối dòng đầu tiên để bạn không cần kiểm tra điều kiện ngược lại cho các dòng tiếp theo.
Ed Morton

1
Xuất hiện văn bản đầu vào ban đầu có thêm một dòng trống trong đó, mà tôi đã mã hóa. Kể từ khi được chỉnh sửa, vì vậy chỉ cần thay đổi NR > 2thành NR > 1.
Jeff Schaller

1
Cảm ơn bạn vì mẹo "chơi gôn", Sundeep! Tôi nghĩ rằng tôi thích vòng lặp 'for' rõ ràng vì nó xếp hàng trực quan / logic với vòng lặp 'for' trong cơ thể.
Jeff Schaller

1
@ fusion.slope, hoặc chuyển toàn bộ mã trong một đối số được trích dẫn đơn lẻ awkhoặc dán mã vào một tệp và chạy nó vớiawk -f that.script.file input-file
Jeff Schaller

6

Một số khác với perl

$ perl -lane 'if($. == 1){ @h=@F }
              else{@i = grep {$F[$_]==1} (0..$#F); print join ",",@h[@i]}
             ' ip.txt
E,I
D
D
A
A,C,G
A,D,H
A,E,F,G
  • -atùy chọn để phân chia dòng đầu vào trên khoảng trắng, có sẵn trong @Fmảng
  • if($. == 1){ @h=@F } lưu tiêu đề nếu dòng đầu tiên
  • @i = grep {$F[$_]==1} (0..$#F) lưu chỉ mục nếu mục nhập là 1
  • print join ",",@h[@i]chỉ in các chỉ mục từ mảng tiêu đề bằng cách sử dụng ,dấu phân cách

4

Vẫn cho sự thú vị của nó, một zshphiên bản:

{
   read -A a  &&
   while read -A b; do
     echo ${(j<,>)${(s<>)${(j<>)a:^b}//(?0|1)}}
   done
} < file
  • ${a:^b} nén hai mảng, do đó bạn nhận được A 0 B 0 C 0 D 0 E 1 F 0 G 0 H 0 I 1
  • ${(j<>)...} nối các phần tử không có gì ở giữa để nó trở thành A0B0C0D0E1F0G0H0I1
  • ${...//(?0|1)}chúng tôi loại bỏ ?01từ đó để nó trở thành EI:
  • ${(s<>)...} phân chia không có gì để có được một mảng của một phần tử trên mỗi chữ cái: EI
  • ${(j<,>)...}tham gia với những người có ,-> E, tôi.

Đây chỉ là một bash đơn giản phải không?
fusion.slope

1
@ fusion.slope, Không, đó là zshmột vỏ khác với bash(và mạnh hơn nhiều, và với thiết kế tốt hơn nhiều nếu bạn hỏi tôi). bashđã chỉ mượn một phần nhỏ của zsh'tính năng s (như {1..4}, <<<, **/*) không phải là những đề cập ở đây, Hầu hết bash' s tính năng nếu không thì vay mượn từ ksh.
Stéphane Chazelas

3

Một giải pháp awk khác :

awk 'NR==1{ split($0,a); next }   # capture and print `header` fields
     { for (i=1;i<=NF;i++)         # iterating through value fields `[0 1 ...]`
           if ($i) { printf "%s",(f?","a[i]:a[i]); f=1 } 
       f=0; print "" 
     }' file

Đầu ra:

E,I
D
D
A
A,C,G
A,D,H
A,E,F,G

2

Đây là một giải pháp trong Perl:

use strict;

my @header = split /\s+/, <>;
<>; ## Skip blank line
while (<>) {
    my @flags = split /\s+/;
    my @letters = ();
    for my $i (0 .. scalar @flags - 1) {
        push @letters, $header[$i] if $flags[$i];
    }

    print join(',', @letters), "\n";
}

Nó hoạt động bằng cách đọc các cột tiêu đề thành một mảng và sau đó, đối với mỗi hàng dữ liệu, sao chép tên cột vào một mảng đầu ra nếu cột dữ liệu phù hợp đánh giá là đúng. Các tên cột sau đó được in phân tách bằng dấu phẩy.


2

Một sedtrong những niềm vui của nó:

sed '
  s/ //g
  1{h;d;}
  G;s/^/\
/
  :1
    s/\n0\(.*\n\)./\
\1/
    s/\n1\(.*\n\)\(.\)/\2\
\1/
  t1
  s/\n.*//
  s/./&,/g;s/,$//'

Với GNU sed, bạn có thể làm cho nó dễ đọc hơn một chút với:

sed -E '
  s/ //g # strip the spaces

  1{h;d} # hold the first line

  G;s/^/\n/ # append the held line and prepend an empty line so the
            # pattern space becomes <NL>010101010<NL>ABCDEFGHI we will
            # build the translated version in the part before the first NL
            # eating one character at a time off the start of the
            # 010101010 and ABCDEFGHI parts in a loop:
  :1
    s/\n0(.*\n)./\n\1/     # ...<NL>0...<NL>CDEFGHI becomes
                           # ...<NL>...<NL>DEFGHI (0 gone along with C)

    s/\n1(.*\n)(.)/\2\n\1/ # ...<NL>1...<NL>CDEFGHI becomes
                           # ...C<NL>...<NL>DEFGHI (1 gone but C moved to 
                           #                        the translated part)
  t1 # loop as long as any of those s commands succeed

  s/\n.*// # in the end we have "ADG<NL><NL>", strip those NLs

  s/./,&/2g # insert a , before the 2nd and following characters'

Một phiên bản ngắn hơn một chút, giả sử luôn có cùng một số chữ số trên mỗi dòng:

sed -E '
  s/ //g
  1{H;d}
  G
  :1
    s/^0(.*\n)./\1/
    s/^1(.*\n)(.*\n)(.)/\1\3\2/
  t1
  s/\n//g
  s/./,&/2g'

Tương tự như trên, ngoại trừ chúng ta hoán đổi các phần dịch và chỉ mục cho phép một số tối ưu hóa.


nếu bạn có thể giải thích sẽ tốt cho cộng đồng. Cảm ơn trước
fusion.slope

1
@ fusion.slope, xem chỉnh sửa.
Stéphane Chazelas

đẹp vòng lặp với lệnh t1!
fusion.slope

1

python3

python3 -c '
import sys
header = next(sys.stdin).rstrip().split()
for line in sys.stdin:
  print(*(h*int(f) for (h, f) in zip(header, line.rstrip().split()) if int(f)), sep=",")

  ' <file
E,I
D
D
A
A,C,G
A,D,H
A,E,F,G

0

Dung dịch bash nguyên chất:

read -a h
while read -a r
do (
    for i in ${!r[@]}
    do 
        (( r[i] == 1 )) && y[i]=${h[i]}
    done
    IFS=,
    echo "${y[*]}")
done

3
Hãy giải thích làm thế nào điều này giải quyết vấn đề.
Scott

Điều đó được để lại như một bài tập cho người đọc. Giả sử kiến ​​thức bash cơ bản LESS="+/^ {3}Array" man bashsẽ cung cấp tất cả thông tin cần thiết cho mảng bash. Bạn có thể tự do chỉnh sửa câu trả lời để thêm bất kỳ sự làm rõ hữu ích nào.
David Ongaro

-1
 void Main(string[] args)
        {
            int[,] numbers = new int[,]
            {
            {0, 0, 0, 0, 1, 0, 0, 0, 1},
            {0, 0, 0, 1, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 1, 0, 0, 0, 1, 0, 0},
            {1, 0, 0, 1, 0, 0, 0, 1, 0},
            {1, 0, 0, 0, 1, 1, 1, 0, 0}
            };
            string letters = "ABCDEFGHI";
            for (int row = 0; row < 7; row++)
            {
                for (int col = 0; col < 9; col++)
                {
                    if (numbers[row, col] == 1)
                        Console.Write(letters[col]);
                }
                Console.WriteLine();
            }
        }

3
Hãy giải thích những gì nó làm và làm thế nào nó hoạt động.
Scott

cũng là ngôn ngữ
fusion.slope
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.