첫 번째 열과 일치하는 행의 경우 두 번째 열을 사용하여 "X 참조 Y" 목록을 만듭니다.

첫 번째 열과 일치하는 행의 경우 두 번째 열을 사용하여 "X 참조 Y" 목록을 만듭니다.

위키데이터의 경우 QuickStatments를 만들어 동일한 이름의 요새에 P1889 값을 할당하고 싶습니다. 이는 다음과 같이 단순화될 수 있습니다.

다음 내용이 포함된 파일의 경우:

Fort George,Q12
Fort George,Q56
Fort George,Q678
Fort Anne,Q3

첫 번째 열에서 정확한 일치를 수행한 다음 X가 Y를 볼 때 일치하는 모든 쌍을 출력하고 싶습니다.

Q12 see also Q56
Q12 see also Q678
Q56 see also Q12
Q56 see also Q678
Q678 see also Q12
Q678 see also Q56

파일은 첫 번째 열을 기준으로 정렬됩니다. 이건 뭔가 이상한 문제처럼 느껴지지만, 쓰기를 포기했어요

답변1

사용밀러( mlr)는 먼저 첫 번째 필드를 기반으로 두 번째 필드를 축소한 다음(실제로는 정렬이 필요하지 않음) 축소된 두 번째 필드를 반복하여 문자열 조합을 인쇄합니다.

$ mlr --csv -N nest --ivar : -f 2 then put -q 'a = splita($2, ":"); for (i in a) { for (j in a) { i != j { print i . " see also " . j } } }' file
Q12 see also Q56
Q12 see also Q678
Q56 see also Q12
Q56 see also Q678
Q678 see also Q12
Q678 see also Q56

실행 후 중간 결과는 nest다음과 같습니다.

$ mlr --csv -N nest --ivar : -f 2 file
Fort George,Q12:Q56:Q678
Fort Anne,Q3

put표현은 아름답습니다.

a = splita($2, ":");

for (i in a) {
    for (j in a) {
        i != j { print i . " see also " . j }
    }
}

동일한 패턴을 따를 수 있지만 awk먼저 입력이 다음과 같다고 가정해야 합니다.단순한CSV(즉, 삽입된 쉼표나 줄바꿈 등을 포함하지 않음)는 더욱 장황해집니다.

$ awk -F , '{ d[$1] = d[$1] == "" ? $2 : d[$1] ":" $2 } END { for (k in d) { split(d[k],a,":"); for (i in a) for (j in a) if (i != j) printf "%s see also %s\n", a[i], a[j] } }' file
Q56 see also Q678
Q56 see also Q12
Q678 see also Q56
Q678 see also Q12
Q12 see also Q56
Q12 see also Q678

코드가 awk아름답게 인쇄됩니다.

{
    d[$1] = d[$1] == "" ? $2 : d[$1] ":" $2
}

END {
    for (k in d) {
        split(d[k], a, ":")

        for (i in a) for (j in a)
            if (i != j) printf "%s see also %s\n", a[i], a[j]
    }
}

in연관 배열의 인덱스에 액세스하기 위해 in을 사용한다고 해서 awk특정 순서가 보장되는 것은 아닙니다. 순서를 수정하려면 대신 산술 루프를 사용하세요. 내용이 너무 길어서 여기서는 보여드리지 않겠습니다.

답변2

이를 지원하는 awk를 사용하고(대부분 지원함) delete array$1의 $2 값을 한 번에 하나씩 메모리에 저장합니다.

awk '
    BEGIN { FS=","; OFS=" see also " }
    $1 != prev {
        delete a
        prev = $1
    }
    {
        for ( i in a ) {
            print $2, i ORS i, $2
        }
        a[$2]
    }
' file
Q56 see also Q12
Q12 see also Q56
Q678 see also Q56
Q56 see also Q678
Q678 see also Q12
Q12 see also Q678

delete a귀하의 awk가 이를 지원하지 않는 경우새로운 awk를 받으세요그러나 split("",a)어떤 awk에서도 사용할 수 있습니다.

sort출력 라인의 순서를 다르게 하려면 출력을 .

답변3

다음 Perl 스크립트는 해시 배열("HoA") 데이터 구조를 사용합니다(참조:perldoc perldsc그리고페릴릴알코올및 관련 문서).

간단히 말해서 "HoA"는 각 요소가 0개 이상의 데이터 요소를 포함하는 인덱스 배열을 저장하는 연관 배열(일명 "해시")입니다(배열은 비어 있을 수 있습니다. 이 경우 각 배열은 이후 최소 하나의 요소를 갖습니다). 추가할 요소가 없으면 생성되지 않습니다.)

#!/usr/bin/perl

while (<<>>) {
  chomp;
  my @F = split /\s*,\s*/;
  push @{ $a{$F[0]} }, $F[1];
}

foreach my $k (sort keys %a) {
  my @values = @{ $a{$k} };
  foreach my $v1 (@values) {
    foreach my $v2 (@values) {
      print "$v1 see also $v2\n" unless $v1 eq $v2;
    }
  };
};

먼저, 입력의 각 줄을 읽고, 각 줄의 끝에서 줄 바꿈을 제거하고, 줄을 @F쉼표 배열로 분할합니다(쉼표에 인접하거나 인접하지 않을 수 있는 공백은 무시). 그런 다음 @F의 첫 번째 요소를 a 해시의 키로 사용하고 %a@F의 두 번째 요소를 %a의 해당 요소에 저장된 배열에 푸시(즉, 추가)합니다.

모든 입력을 읽을 때 %a의 키를 반복한 다음 두 번 반복하고 해당 키 내의 배열에 있는 값을 중첩적으로 반복하여 값이 다른 경우에만 줄을 인쇄합니다.

%a에 대한 키를 정렬했지만 그럴 필요는 없습니다. Perl 해시는 본질적으로 순서가 없기 때문에(대부분의 언어에서처럼) 출력의 일관성을 보장하기 위한 것입니다. 정렬되지 않은 경우 각 실행마다 무작위 순서로 표시됩니다.

@values ​​배열을 정렬할 수도 있지만 (값은 문자와 숫자의 혼합이므로) 자연 정렬 알고리즘(때때로 "버전 정렬"이라고도 함)을 구현하거나정렬::자연또는정렬::버전또는 다른 자연 주문 모듈 중 하나CPAN. 그대로, 읽은 순서대로 값이 출력됩니다.

기술적으로 @values출력 루프의 배열은 필요하지 않습니다. 그냥 @{ $a{$k} }앤 루프로 사용할 수도 있지만 제 생각에는 이 방법이 읽고 이해하기 더 쉽습니다.$v1$v2

스크립트를 다른 이름으로 저장 하고 실행 가능하게 만든 see-also.pl후 샘플 입력에 대한 샘플 출력 chmod:

$ ./see-also.pl input.txt 
Q12 see also Q56
Q12 see also Q678
Q56 see also Q12
Q56 see also Q678
Q678 see also Q12
Q678 see also Q56

답변4

또한 datamashbash 중괄호 확장을 사용하여 데이터를 그룹화하고 조합을 얻을 수도 있습니다. 예를 들면 다음과 같습니다.

<infile datamash -st, --output-delimiter=$'\t' -g1 collapse 2 |
cut -f2                                                       |
while read; do
  bash -c "printf '%s\n' {$REPLY}$'\t'{$REPLY}"
done                                                          |
awk '$1 != $2 { print $1 " see also " $2 }'

관련 정보