видалити дублікати на основі значення іншого стовпця


9

У мене є такий файл:

AA,true
AA,false
BB,false
CC,false
BB,true
DD,true

Я намагаюся шукати дублікати і видаляти рядок, у якого значення стовпця дорівнює true.

як вихід він повинен бути:

AA,false
BB,false
CC,false
DD,true

2
Отже .. зберігайте лише, trueякщо це перший екземпляр першого стовпця?
DopeGhoti

1
@ РоманПерехрест Ймовірно, це запис Uniq і надруковано "як є"
Джордж Васильоу

@RomanPerekhrest, оскільки DD, true не є дублікатом, у нас немає іншого рядка з DD, false.
Hani Gotc

AA,true AA,false AA,false AA,falseЯкий вихід повинен бути у цьому випадку? Я розумію, що цей рядок слід видаляти, лише якщо він має дублікат і містить trueодночасно. У falseбудь-якому випадку всі ряди повинні залишатися недоторканими. Тобто в цьому випадку AA, trueбуде видалено лише те. Але всі відповіді залишають лише один рядок - AA,false. Просто цікаво :)
MiniMax

Відповіді:


9
awk -F, '$2 == "false" {data[$1]=$2 } $2=="true" { if ( data[$1]!="false" ) { data[$1]=$2 } } END { OFS=","; for (item in data) { print item,data[item] }}' input

Щоб розгорнути сценарій вертикально для пояснення:

BEGIN {
   FS=","         # Set the input separator; this is what -F, does.
}
$2 == "false" {    # For any line whose second field is "false", we
   data[$1]=$2     # will use that value no matter what.
}
$2=="true" {                    # For lines whose second field is "true",
   if ( data[$1]!="false" ) {   # only keep if if we haven't yet seen a
      data[$1]=$2               # "false"
   }
}
END {                           # Now that we have tabulated our data, we
   OFS=","                      # can print it out by iterating through 
   for (item in data) {         # the array we created.
      print item,data[item]
   }
}

@DopeGhoti добре пояснив! У вас є +1 для цього.
Валентин Байрамі

14

Проста версія:

sort input.txt | awk -F, '!a[$1]++'

"false" сортує в алфавітному порядку перед "true", і команда Awk тут просто зберігає перший рядок лише для кожного окремого першого значення поля.

Якщо ви хочете зберегти "true" замість "false", відмініть його сортуванням, переведіть його в ту саму команду Awk і після цього повторно сортуйте її.


1
також, якщо -uє можливість,sort input.txt | sort -t, -u -k1,1
Sundeep

2
@Sundeep навіщо використовувати два sortдзвінки? Чому б не просто sort -ut, -k1,1 input.txt ?
тердон

2
@terdon, оскільки -uзбереже перший рядок, знайдений у вхідному файлі серед дублікатів ... для даного випадку, вхід має бути відсортований, перш ніж -uможна буде застосувати ... для наприклад: AA,trueбуде надруковано замість того, що AA,falseвін з’являється першим у даному зразку .. та сама причина, чому awk -F, '!a[$1]++'самостійно не вирішить цю проблему
Sundeep

5
perl -F, -lane '
   exists $h{$F[0]} or $h[$h{$F[0]}=@h]=$_;
   $h=$_; /,false$/ or $_=$h for $h[$h{$F[0]}];
   END{ print for @h; }
' duplicates.file

Структури даних:

  • Хеш %h, ключі яких є першими полями (AAA, BBB, CCC тощо) та відповідні значення - це цифри, що вказують на порядок, у якому були зустрічаються ключі. Так, наприклад, ключ AAA => 0, ключ BBB => 1, ключ CCC => 2.
  • Масив @h, елементи якого - це рядки, що містяться в порядку друку. Отже, якщо в даних знайдеться і істинне, і неправдиве значення, помилкове значення перейде в масив. OTW, якщо є один тип даних, то це буде присутнє.

Ще один спосіб використання sed GNU:

sed -Ee '
   G
   /^([^,]*),(false|true)\n(.*\n)?\1,\2(\n|$)/ba
   /^([^,]*)(,true)\n(.*\n)?\1,false(\n|$)/ba
   /^([^,]*)(,false)\n((.*\n)?)\1,true(\n|$)/{
      s//\3\1\2\5/;h;ba
   }
   s/([^\n]*)\n(.*)$/\2\n\1/;s/^\n*//
   h;:a;$!d;g
' duplicates.file

FWIW, еквівалентний код POSIX для вищевказаного коду GNU наведено нижче:

sed -e '
   G

   /^\([^,]*\),\(false\)\n\(.*\n\)\{0,1\}\1,\2$/ba
   /^\([^,]*\),\(false\)\n\(.*\n\)\{0,1\}\1,\2\n/ba

   /^\([^,]*\),\(true\)\n\(.*\n\)\{0,1\}\1,\2$/ba
   /^\([^,]*\),\(true\)\n\(.*\n\)\{0,1\}\1,\2\n/ba

   /^\([^,]*\),true\n\(.*\n\)\{0,1\}\1,false$/ba
   /^\([^,]*\),true\n\(.*\n\)\{0,1\}\1,false\n/ba

   /^\([^,]*\)\(,false\)\n\(\(.*\n\)\{0,1\}\)\1,true$/{
      s//\3\1\2/
      h
      ba
   }
   /^\([^,]*\)\(,false\)\n\(\(.*\n\)\{0,1\}\)\1,true\n/{
      s//\3\1\2\n/
      h
      ba
   }

   y/\n_/_\n/
   s/\([^_]*\)_\(.*\)$/\2_\1/;s/^_*//
   y/\n_/_\n/

   h;:a;$!d;g
' duplicates.file

Пояснення

  • У цьому методі ми зберігаємо результат, який буде остаточно надрукований у просторі утримування.
  • Для кожного прочитаного рядка ми додаємо простір утримування до простору шаблону для дослідження поточного рядка відносно існуючого стану простору утримування.
  • Зараз під час цього порівняння може статися 5 речей:
    • a) Поточний рядок збігається десь у рядку утримування & false: false.
      • [ДІЯ] Оскільки такий самий хибний стан знайдено, то нічого не робіть.
    • b) Поточна відповідність рядка десь у рядку утримування & true: true.
      • [ДІЯ] Оскільки такий самий справжній стан знайдено, то нічого не робіть.
    • c) Поточна відповідність рядка десь у рядку утримування & true: false.
      • [ДІЯ] Оскільки помилковий стан вже існує, нічого не робіть.
    • г) Поточна відповідність рядка десь у рядку утримування & false: true.
      • [АКЦІЯ] Це передбачає певну роботу, в якій нам потрібно замінити помилкову лінію в точно такому ж місці, де знаходиться істина.
    • д) Поточний рядок НЕ відповідає жодному місці у рядку утримування.
      • [ДІЯ] Перемістіть поточний рядок до кінця.

Результати

AA,false
BB,false
CC,false
DD,true

3

Для кожного рядка введення зберігайте значення другого поля в асоціативному масиві a(використовуючи перше поле як ключ масиву) ТОЛЬКО, якщо ми ще не зберегли значення falseдля цього ключа. Використовуйте ,як для розділювача поля вводу, так і для виводу. Роздрукуйте масив після того, як ми прочитали всі рядки введення.

$ awk -F, -v OFS=, 'a[$1] != "false" { a[$1] = $2 };
                    END { for (i in a) {print i,a[i]} }' truefalse.txt
AA,false
BB,false
CC,false
DD,true

Істотна різниця між цією версією DopeGhoti полягає в тому, що ця версія зовсім не переймається значенням $2, вона піклується лише про значення, якщо воно є, значення a[$1].


1

Двопрохідне sortрішення

sort -k1,1 -k2,2 -t, file | sort -k1,1 -t, -u

Перший sortпрохід кластеризує записи по полях 1із falseзаписами, що передують trueкожному блоку записів, що мають спільне 1значення поля . Другий sortпрохід налаштовується для отримання одного запису на кожне окреме значення в межах 1ввічливості поля -u. Оскільки -uмає на увазі стабільне сортування, один отриманий таким чином запис є першим записом, що зустрічається для кожного окремого значення всередині поля 1- що є записом falseу другому полі завдяки роботі, виконаній першим sortпроходом

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.