Об'єднайте рядки за першим стовпцем за допомогою awk або sed


12

Як я можу використовуватись awkу наступній ситуації?

Я хочу об'єднати рядки, які починаються з того ж стовпця. Тільки перший стовпець зберігається після з'єднання (в даному випадку aaa, www, hhh).

Файл може бути розділений пробілом або вкладками.

Приклад введення:

aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL

Бажаний вихід:

aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL

Передумовою цього є те, що я хочу створити дуже просту базу даних на основі файлів, де перший стовпець завжди є ідентифікатором для сутності. Усі рядки на основі одного стовпчика ідентифікатора є об'єднаними.


1
звідки uuuвзявся рядок (у висновку)?
saeedn

Вибач, моя погана. Я відредагую.
крихітний

Відповіді:


8

Для отримання перших стовпців у кожному рядку за допомогою awk ви можете зробити наступне:

< testfile awk '{print $1}'
aaa
aaa
aaa
www
hhh
hhh

Це ваші ключі для решти рядків. Таким чином, ви можете створити хеш-таблицю, використовуючи перший стовпець як ключ, а другий стовпець рядка як значення:

< testfile awk '{table[$1]=table[$1] $2;} END {for (key in table) print key " => " table[key];}'
www => yyy
aaa => bbbNULLbbb
hhh => 111111

Щоб отримати весь інший рядок, починаючи з колонки 2, потрібно зібрати всі стовпці:

< testfile awk '{line="";for (i = 2; i <= NF; i++) line = line $i " "; table[$1]=table[$1] line;} END {for (key in table) print key " => " table[key];}'
www => yyy hhh NULL NULL NULL NULL 
aaa => bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc    NULL NULL NULL NULL 
hhh => 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL 

Привіт, так, це дійсно потребувало розбиття хеш-таблиць. Дякую!
крихітний

2
@tiny - я припускав, що замовлення потрібно зберегти. Чи це не так (ця відповідь створює замовлення, відповідне механізму хешування, а не ваше первісне замовлення)?
ire_and_curses

3

Хтось ще може відповісти awk або sed, але версія Python проста та може бути корисною для вас.

#!/usr/bin/env python

input_file = 'input.dat'
in_fh      = open(input_file, 'r')

input_order = []
seen        = {}
for line in in_fh:    
    # Remove the newline character...
    line = line[:-1]

    # Separate the first column from the rest of the line...
    key_col, sep, rest_of_line = line.partition(" ")
    rest_of_line = sep + rest_of_line  

    # If we've seen this key already, concatenate the line...
    if key_col in seen:
        seen[key_col] += rest_of_line
    # ...otherwise, record the ordering, and store the new info
    else:
        input_order.append(key_col)
        seen[key_col] = rest_of_line

in_fh.close()

# Dump the ordered output to stdout
for unique_col in input_order:
    print unique_col + seen[unique_col]

Дуже круто. З моїм нульовим досвідом python мені навіть вдалося відредагувати сценарій, щоб він взяв перший аргумент як ім'я вхідного файлу :)
крихітний

2

Це більше цікаве застосування coreutils, я підозрюю, що це не дуже ефективно з великим входом, оскільки він викликає приєднання для кожного рядка вхідних даних.

touch outfile
while read; do
  join -a1 -a2 outfile <(echo $REPLY) > tmp
  mv tmp outfile
done < infile

Щоб підвищити свою ефективність, може допомогти заощадження outfileта tmpрамні диск.

Редагувати

Або без тимчасових файлів:

out=""
while read; do
  out=$(join -a1 -a2 <(echo -n "$out") <(echo -n "$REPLY"))
done < infile

echo "$out"

2

А ось однолінійний PERL:

$ perl -e 'my %h; while(<>){chomp; @a=split(/\s+/); $k=shift(@a); $h{$k}.=join(" ", @a) . " "; } map{$h{$_}=~s/\s*$//; print "$_ $h{$_}\n}keys(%hash);' infile
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.