Как сравнить 2 файла со случайными числами в непоследовательном порядке? - PullRequest
12 голосов
/ 21 июня 2020

Есть 2 файла с именами compare 1.txt и compare2.txt, в которых случайные числа находятся в непоследовательном порядке

cat compare1.txt

57
11
13
3
889
014
91

cat compare2.txt

003
889
13
14
57
12
90

Цель

  1. Вывести список всех чисел, которые присутствуют в compare1, но не в compare 2, и наоборот

  2. Если какое-либо число имеет ноль в своем префиксе, игнорируйте нули при сравнении (в основном абсолютное значение числа должно отличаться, чтобы считаться несоответствием) Пример - 3 следует рассматривать как соответствие с 003 и 014 следует рассматривать сопоставление с 14, 008 с 8 et c

Примечание. Совпадение не обязательно должно обязательно происходить в той же строке. Число, представленное в первой строке в compare1, должно считаться совпадающим, даже если такое же число присутствует не в первой строке в compare2

Ожидаемый результат

90
91
12
11

PS (я не t обязательно нужен этот точный порядок в ожидаемом выводе, подойдут только эти 4 числа в любом порядке)

Что я пробовал?

Очевидно, у меня не было надежды получить правильное второе условие, Я пытался выполнить только первое условие, но не смог получить правильных результатов. Я пробовал эти команды

grep -Fxv -f compare1.txt compare2.txt && grep -Fxv -f compare2.txt compare1.txt
cat compare1.txt compare2.txt | sort |uniq

Edit - решение Python тоже подойдет

Ответы [ 8 ]

11 голосов
/ 21 июня 2020

Не могли бы вы попробовать следующие, написанные и протестированные с показанными образцами в GNU awk.

awk '
{
  $0=$0+0
}
FNR==NR{
  a[$0]
  next
}
($0 in a){
  b[$0]
  next
}
{ print }
END{
  for(j in a){
    if(!(j in b)){ print j }
  }
}
'  compare1.txt compare2.txt

Пояснение: Добавление подробного объяснения вышеизложенного.

awk '                                ##Starting awk program from here.
{
  $0=$0+0                            ##Adding 0 will remove extra zeros from current line,considering that your file doesn't have float values.
}
FNR==NR{                             ##Checking condition FNR==NR which will be TRUE when 1st Input_file is being read.
  a[$0]                              ##Creating array a with index of current line here.
  next                               ##next will skip all further statements from here.
}
($0 in a){                           ##Checking condition if current line is present in a then do following.
  b[$0]                              ##Creating array b with index of current line.
  next                               ##next will skip all further statements from here.
}
{ print }                                   ##will print current line from 2nd Input_file here.
END{                                 ##Starting END block of this code from here.
  for(j in a){                       ##Traversing through array a here.
    if(!(j in b)){ print j }         ##Checking condition if current index value is NOT present in b then print that index.
  }
}
'  compare1.txt compare2.txt         ##Mentioning Input_file names here.
6 голосов
/ 21 июня 2020

Вот как сделать то, что вы хотите, просто используя awk:

$ awk '{$0+=0} NR==FNR{a[$0];next} !($0 in a)' compare1.txt compare2.txt
12
90

$ awk '{$0+=0} NR==FNR{a[$0];next} !($0 in a)' compare2.txt compare1.txt
11
91

, но это задача, для которой comm существует, поэтому вот как вы можете использовать это, чтобы получить все различия и общие строки в один раз. В следующем выводе col1 - только compare1.txt, col2 - только compare2.txt, col3 - общий для обоих файлов:

$ comm <(awk '{print $0+0}' compare1.txt | sort) <(awk '{print $0+0}' compare2.txt | sort)
11
    12
        13
        14
        3
        57
        889
    90
91

или для получения каждого результата отдельно:

$ comm -23 <(awk '{print $0+0}' compare1.txt | sort) <(awk '{print $0+0}' compare2.txt | sort)
11
91

$ comm -13 <(awk '{print $0+0}' compare1.txt | sort) <(awk '{print $0+0}' compare2.txt | sort)
12
90

$ comm -12 <(awk '{print $0+0}' compare1.txt | sort) <(awk '{print $0+0}' compare2.txt | sort)
13
14
3
57
889
3 голосов
/ 22 июня 2020

Учитывая эти два файла в Python, вы можете использовать симметричное c различие наборов:

with open(f1) as f:         # read the first file into a set
    s1={int(e) for e in f}
    
with open(f2) as f:         # read the second file into a set
    s2={int(e) for e in f}
    
print(s2 ^ s1)              # symmetric difference of those two sets
# {11, 12, 90, 91}

Что можно упростить до:

with open(f1) as f1, open(f2) as f2:
    print({int(e) for e in f1} ^ {int(e) for e in f2})

Подробнее про Python наборы в документах

1 голос
/ 21 июня 2020

Кажется, я где-то слышал, что решение Ruby подойдет, поэтому я дам два, но если Ruby находится в черном списке, по крайней мере один из методов можно легко перевести на язык, указанный в утвержденный список (знание Ruby не требуется). Первый метод использует наборы, которые Ruby реализует с помощью хешей под крышкой. Второй метод использует хеши. Я предоставил последнее, если выбранный язык не поддерживает заданные объекты.

Главное - использовать метод, близкий к O (n) по вычислительной сложности, где n - это сумма размеры двух массивов. Я говорю «близко к» O (n), потому что методы, которые я предлагаю, используют хеши, прямо или косвенно, и поиск ha sh не совсем O (1). Традиционный подход к этой проблеме, перечисление второго массива для каждого элемента первого, и наоборот, имеет вычислительную сложность O (n 2 ).

Нам даны два массивы:

arr1 = ["57", "11", "13", "3", "889", "014", "91"] 
arr2 = ["003", "889", "13", "14", "57", "12", "90"]

Использовать наборы

require 'set'

def not_in_other(a1, a2)
  st = a2.map(&:to_i).to_set
  a1.reject { |s| st.include?(s.to_i) }
end

not_in_other(arr1, arr2) + not_in_other(arr1, arr2)
  #=> ["11", "91", "11", "91"]

Примечание:

a = arr2.map(&:to_i)
  #=> [3, 889, 13, 14, 57, 12, 90] 
a.to_set
  #=> #<Set: {3, 889, 13, 14, 57, 12, 90}> 

Использовать хэши

Шаг 1: Создайте ha sh для каждого массива

def hashify(arr)
  arr.each_with_object({}) { |s,h| h[s.to_i] = s }
end

h1 = hashify(arr1)
  #=> {57=>"57", 11=>"11", 13=>"13", 3=>"03", 889=>"889",
  #    14=>"014", 91=>"91"} 
h2 = hashify(arr2)
  #=> {3=>"003", 889=>"889", 13=>"13", 12=>"12", 14=>"14",
  #    57=>"57", 90=>"90"}

Значение этих хэшей (чьи ключи являются целыми числами) должно быть самоочевидным.

Шаг 2: Определите, какие ключи в каждом га sh отсутствуют в другом га sh

keys1 = h1.keys
  #=> [57, 11, 13, 3, 889, 14, 91] 
keys2.keys
  #=> [3, 889, 13, 12, 14, 57, 90] 

keepers1 = keys1.reject { |k| h2.key?(k) }
  #=> [11, 91] 
keepers2 = keys2.reject { |k| h1.key?(k) }
  #=> [12, 90]

В качестве альтернативы можно написать:

keepers1 = keys1 - keys2
keepers2 = keys2 - keys1

Я ожидаю, что это будет O (n), но это будет зависеть от реализации.

Шаг 3: Получите значения h1 для ключей keepers1 и h2 для ключей keepers2, и объединить их

h1.values_at(*keepers1) + h2.values_at(*keepers2)
  #=> ["11", "91", "12", "90"] 
0 голосов
/ 03 июля 2020

Помимо обработки ведущих нулей, ваша задача может быть решена просто с помощью команды diff и фильтрации ее вывода

diff "$FIRST" "$SECOND" \
        | awk '$1~/[<>]/{print $2}' # Only added or removed lines

Вы можете избавиться от ведущих нулей с помощью bc

FIRST=${1:-first file should be specified}
SECOND=${2:-second file should be specified}
normalize() {
    bc < "$1" | sort --numeric
}
diff <(normalize "$FIRST") <(normalize "$SECOND") \
        | awk '$1~/[<>]/{print $2}'

Обратите внимание, что подстановка процесса синтаксис <(command) - это bashism , вам нужно будет вместо этого использовать временный файл для соответствия POSIX.

0 голосов
/ 21 июня 2020

Другое решение в python:

x = [int(x) for x in open("compare1.txt")]
y = [int(x) for x in open("compare2.txt")]
z = []

for i in x:
    if (i not in y):
        z.append(i)


for i in y:
    if (i not in x):
        z.append(i)

for i in z:
    print(i)
0 голосов
/ 21 июня 2020

Другое альтернативное решение одного из моих друзей в python

list1 = set()
list2 = set()
with open('compare1.txt','r') as file1:
    for line in file1:
        if line != '\n':
            list1.add(int(line))

with open('compare2.txt','r') as file2:
    for line in file2:
        if line != '\n':
            list2.add(int(line))

list3 = list1.symmetric_difference(list2)

for number in list3:
    print(number)
0 голосов
/ 21 июня 2020

Используя python, вы можете сделать следующее:

import csv

def func(file1, file2):
    set1 = read_file_as_set(file1)
    set2 = read_file_as_set(file2)

    union = set1.union(set2) #find union first
    intersection = set1.intersection(set2) #find intersection
    return union.difference(intersection)


def read_file_as_set(file):
    result = set()

    with open(file) as csv_file:
        file_reader = csv.reader(csv_file)

        for line in file_reader:
            result.add(int(line[0]))

    return result

if __name__=='__main__':

    print func("path/to/first/file.csv","path/to/second/file.csv")

Я по существу читаю оба файла как отдельные наборы и возвращаю (file1_set union file2_set) - (file1_set пересечение с file2_set)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...