удалить дубликаты на основе значения другого столбца

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
@RomanPerekhrest Вероятно, потому что это уникальная запись и печатается «как есть»
Джордж Василиу
@RomanPerekhrest, потому что DD, true не является дубликатом, у нас нет другой строки с DD, false.
Хани
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
источник
@ DopeGhoti хорошо объяснил! У тебя есть мой +1 на это.
Валентин Байрами
14

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

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

«false» сортирует в алфавитном порядке перед «true», и здесь команда Awk просто сохраняет первую строку только для каждого отдельного значения первого поля.

Если вы хотите оставить «true» вместо «false», выполните обратную сортировку, передайте ее той же команде Awk и затем снова выполните обратную сортировку.

Wildcard
источник
1
также, если -uопция доступна,sort input.txt | sort -t, -u -k1,1
Sundeep
2
@ Сандип, зачем использовать два sortзвонка? Почему не просто sort -ut, -k1,1 input.txt ?
Тердон
2
@terdon, потому -uчто сохранит первую строку, найденную из входного файла среди дубликатов ... для данного случая вход должен быть отсортирован до того, как его -uможно будет применить ... для ex: 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, если есть один тип данных, то это будет присутствовать.

Другой способ - использовать GNU sed:

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 приведен ниже:

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.
      • [ДЕЙСТВИЕ] Так как это же ложное состояние найдено, то ничего не делать.
    • б) Текущая строка совпадает где-то в линии удержания & 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проходом

Iruvar
источник