разбор строк ruby ​​CSV - PullRequest
       26

разбор строк ruby ​​CSV

3 голосов
/ 07 марта 2012

У меня есть некоторые данные CSV, которые мне нужно обработать, и я не могу найти способ сопоставления дубликатов.

данные выглядят примерно так:

line    id    name   item_1    item_2    item_3    item_4
1      251   john    foo       foo       foo       foo
2      251   john    foo       bar       bar       bar
3      251   john    foo       bar       baz       baz
4      251   john    foo       bar       baz       pat

строки 1-3 в этом случае дубликаты.

line    id    name   item_1    item_2    item_3    item_4
5      347   bill    foo       foo       foo       foo
6      347   bill    foo       bar       bar       bar

в этом случае только строка 5 является дубликатом

line    id    name   item_1    item_2    item_3    item_4
7      251   mary    foo       foo       foo       foo
8      251   mary    foo       bar       bar       bar
9      251   mary    foo       bar       baz       baz

здесь строки 7 и 8 являются дубликатами

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

Я использую Ruby 1.9.3 следующим образом:

require 'csv'
puts "loading data"
people = CSV.read('input-file.csv')

CSV.open("output-file", "wb") do |csv|
    #write the first row (header) to the output file
    csv << people[0]
    people.each do |p|
        ... logic to test for dupe ...
        csv << p.unique
    end
end

Ответы [ 3 ]

3 голосов
/ 07 марта 2012

Во-первых, в вашем коде есть небольшая ошибка. Вместо:

csv << people[0]

Вам нужно будет сделать следующее, если вы не хотите изменять свой код цикла:

csv << people.shift

Теперь следующее решение добавит только первое вхождение человека, отбрасывая любые последующие дубликаты, как определено идентификатором (как я предполагаю, идентификаторы уникальны).

require 'csv'
puts "loading data"
people = CSV.read('input-file.csv')
ids = [] # or you could use a Set

CSV.open("output-file", "wb") do |csv|
  #write the first row (header) to the output file
  csv << people.shift
  people.each do |p|
    # If the id of the current records is in the ids array, we've already seen 
    # this person
    next if ids.include?(p[0])

    # Now add the new id to the front of the ids array since the example you gave
    # the duplicate records directly follow the original, this will be slightly
    # faster than if we added the array to the end, but above we still check the
    # entire array to be safe
    ids.unshift p[0]
    csv << p
  end
end

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

Это будет выглядеть так:

require 'csv'
puts "loading data"
people = CSV.read('input-file.csv')
previous_id = nil

CSV.open("output-file", "wb") do |csv|
  #write the first row (header) to the output file
  csv << people.shift
  people.each do |p|
    next if p[0] == previous_id
    previous_id = p[0]
    csv << p
  end
end
1 голос
/ 07 марта 2012

Похоже, вы пытаетесь получить список уникальных предметов, связанных с каждым человеком, где человек идентифицируется по идентификатору и имени. Если это правильно, вы можете сделать что-то вроде этого:

peoplehash = {}
maxitems = 0
people.each do |id, name, *items|
    (peoplehash[[id, name]] ||= []) += items
peoplehash.keys.each do |k|
    peoplehash[k].uniq!
    peoplehash[k].sort!
    maxitems = [maxitems, peoplehash[k].size].max

Это даст вам такую ​​структуру, как:

{
    [251, "john"] => ["bar", "bat", "baz", "foo"],
    [347, "bill"] => ["bar", "foo"]
}

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

0 голосов
/ 07 марта 2012

Вы можете использовать 'uniq'

irb(main):009:0> row= ['ruby', 'rails', 'gem', 'ruby']
irb(main):010:0> row.uniq
=> ["ruby", "rails", "gem"]
or 

row.uniq!
=> ["ruby", "rails", "gem"]

irb(main):017:0> row
=> ["ruby", "rails", "gem"]

irb(main):018:0> row = [1,      251,   'john',    'foo',       'foo',       'foo',       'foo']
=> [1, 251, "john", "foo", "foo", "foo", "foo"]
irb(main):019:0> row.uniq
=> [1, 251, "john", "foo"]
...