2012-04-28 9 views
0

私はアイデアを探していますが、シェル(Linux)の次の問題の完全な解決策ではありません。最高の解決策は何ですか? (awk、while-loop、sed ....)2つのファイルをkey - > values構造でマージする

同じ行構造を持つ2つのファイル:key-value-valueがあります。私はこれらの2つのファイルをマージしたい。値が存在しない場合、スクリプトは新しい行を挿入します。存在する場合、スクリプトは値を更新します(追加することにより)。

Example: 
File 1: 

john-15-40 
doo-10-91 
mary-14-19 
foo-11-0 

File 2: 

foo-110-10 
john-22-11 
ghost-1000-1000 

Result: 
foo-121-10 
john-37-51 
ghost-1000-1000 
doo-10-91 
mary-14-19 

どうすればいいですか?

答えて

4

awkを使用した単純な

awk ' 
    BEGIN {FS = OFS = "-"} 
    {v1[$1] += $2; v2[$1] += $3} 
    END {for (key in v1) {print key, v1[key], v2[key]}} 
' F1 F2 
+0

+1美しい解決策を探しています。 – kev

+0

これは美しいです – flatronka

+0

美容は主観的で、時には肌だけ深く、知性が低いです。ここで、「美」とは、文字列の値を数値として扱い、定義されていない変数を増やすことができるという点にあります。変数のスペルを間違えた場合は、awkからの警告の一言もありません。文字列に数字以外のものが含まれている場合、それはちょうどゼロです。wee ... awk '{print "a" + 1}' 'はうれしく1を返します。問題はありません。あなたの「美しいプログラム」で常に「美しい入力」を使用してください。 – Kaz

1

アソシエイティブな配列を持つ言語が必要です。あなたの仕事はどのスクリプト言語でも非常に簡単ですが、perlとawkはテキストファイルを行ごとに処理するのに特に適しています。

擬似コード:

read line from file1, file2 
split line to key and values 
if there are no key in hash 
    add key and values 
else 
    add values and print key/values 
+0

これは実際に、バッシュ4ネイティブ連想配列を持ってい@hunor良い解決策のおかげではなく、賢いシェルにする(Linux)、 – flatronka

+0

です。古いバージョンの言語で動作する必要がない場合は、これをネイティブシェルで実行できます。 –

+0

@Andrey no、これのために連想配列は必要ありません - 処理する前に入力ストリームをソートする場合。 –

1

私はあなたがPHPでそれを要求していない知っているが、それが役立つかもしれません。あなたが好む場合は別の言語で似た何かがおそらくあります:

<?PHP 

$file_handle = fopen("file1", "r"); 

while (!feof($file_handle)) { 
$line_of_text = fgets($file_handle); 
list($name,$value1,$value2) = explode('-', $line_of_text); 
$file1[$name]=array($value1,$value2); 
} 
fclose($file_handle); 
// repeate for file2 
//then use the 2 arrays, $file1[] and $file2[] to rewrite the file as 'file3' or whatever. 
//Checking for duplicates and doing the math. 
?> 
+0

ソリューションのおかげで、私はPHPが大好きです、それはAndrey Yazu擬似コードに似ています – flatronka

+0

私は同じことを考えました。彼は私が書いている間に投稿した。 – TecBrat

+0

間違いなく、私はスマートなシェル(Linux)ベースの解決策 – flatronka

1

これはバッシュ4でネイティブに実行できます。

#!/bin/bash 
declare -A vals_one vals_two 
while IFS=- read key val1 val2; do 
    if [[ ${vals_one["$key"]} ]] ; then 
    vals_one["$key"]=$((${vals_one["$key"]} + val1)) 
    vals_two["$key"]=$((${vals_two["$key"]} + val2)) 
    else 
    vals_one["$key"]=$val1 
    vals_two["$key"]=$val2 
    fi 
done < <(cat input1.txt input2.txt) 
for key in "${!vals_one[@]}"; do 
    printf '%s-%s-%s\n' "$key" "${vals_one[$key]}" "${vals_two[$key]}" 
done 

(注)このアプローチことややメモリ非効率です。よりメモリ効率の良いアプローチは、ファイルをマージする前にソートします(GNU sortは、ソートした内容がメモリに収まらない場合に一時ファイルを生成することができます。また、この後者の形態は、連想配列を必要とせず、古いはbashのバージョン(または、いくつかの適応と、他のシェル)で動作する

#!/bin/bash 

function merge_inputs { 
    IFS=- read key val1 val2 
    while IFS=- read new_key new_val1 new_val2; do 
     if [[ $key = "$new_key" ]] ; then 
     val1=$((val1 + new_val1)) 
     val2=$((val2 + new_val2)) 
     else 
     printf '%s-%s-%s\n' "$key" "$val1" "$val2" 
     key=$new_key 
     val1=$new_val1 
     val2=$new_val2 
     fi 
    done 
    printf '%s-%s-%s\n' "$key" "$val1" "$val2" 
} 
sort input1.txt input2.txt | merge_inputs 

:従って、専用メモリ内の時間で2つの行を格納するために必要。

+0

あなたの仕事に感謝します:d、私はそれが好き、素敵です – flatronka

1

私はglennのソリューションを好きです。さらに、tall thinソリューションがあります。

2つのファイル:1.txt2.txtがある場合。

sort {1,2}.txt | 
awk -F- -vOFS=- ' 
NR==1{ 
    x=$1 
} 
x==$1{ 
    y+=$2 
    z+=$3 
    next 
} 
{ 
    print x,y,z; 
    x=$1 
    y=$2 
    z=$3 
} 
END{ 
    print 
}' 
関連する問題