Перейти к содержимому

Фото
- - - - -

Ruby CSV merge rows

Ruby CSV

Лучший Ответ arthur1981, 01 Сентябрь 2014 - 10:50

На совсем скорую руку набросал коду:

 

c.group_by {|e| [ e[:url1], e[:url2] ] }.map {|_,v| v = v.map {|a| a.reject {|_,y| y.blank?} };  v.reduce({}, :merge)}

 

Где c - массив, который содержит все rows. Т.е тебе из файла вначале надо все вычитать в один массив. 

Тут главная фишка в том, чтобы перед слиянием из слияемых хэшей выкинуть те значения, которые пустые. Иначе поведение может быть непредсказуемым при слиянии. 
Просто сказу сразу, что если ты делаешь слияние двух хешей, и у них в одном месте в одном ключе разные значение, то конечное значение будет равно последнему.

 

{:one =>1}.merge({:one => 2}) # => {:one => 2}

 

Вот )) Не уверен, конечно, что идеально работает, но поидее должно )) Если че - потом еще взгляну, допилю, если надо.

Большое спасибо за помощь!

Перейти к полному сообщению


  • Вы не можете создать новую тему
  • Please log in to reply
8 ответов в этой теме

#1 arthur1981

arthur1981
  • Новобранец
  • 14 сообщений

Отправлено 31 Август 2014 - 21:15

Добрый день! Может кто знает как соединить ряды (rows) CSV файла на руби в по такому принципу

Я ввожу CSV фаил с такой таблицей:

---url1------------|------url2------------|---date visited----|------time visited---|----ip from-----|-links cliked 
--myhome.com--|--friendhome.com--|---16/02/10-------|-----------------------|-----------------|--------------
--myhome.com--|--friendhome.com--|--------------------|--------12.45---------|-192.168.1.1-- |--------------
--myhome.com--|--friendhome.com--|--------------------|-----------------------|-----------------|-link1-------
--someone.com--|--bigimot.com------|----13/10/14------|-----------------------|-----------------|--link2
--someone.com--|---bigimot.com-----|--------------------|--------11.15---------|--199.168.1.1--|--------------

Вот что должно получится:

---url1------------|------url2------------|---date visited----|------time visited---|----ip from-----|-links cliked 
--myhome.com--|--friendhome.com--|---16/02/10-------|--------12.45--------|-192.168.1.1-- |--link1-------
--someone.com--|--bigimot.com------|----13/10/14------|--------11.15--------|--199.168.1.1--|--link2----

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

 

Вот мой код

require 'csv'
 
def merge_csv_rows(orig_file, merged_file, keys)
  rows = Hash.new { |h, k| h[k] = Hash.new }
 
  CSV.open orig_file, 'r', headers: true, col_sep: ?;, :quote_char => "\x00" do |orig|
    orig.each do |row|
      key = row.values_at(*keys)
   values = row.to_hash.reject {|k,v| v.nil? }
   
    rows[key].merge!values.to_hash #do |_,old, new|
    end
    CSV.open merged_file, 'w', headers: orig.headers, write_headers: true do |merged|
      rows.each do |key, values|
        fields = orig.headers.map do |header|
          if  keys.include? header
            key[keys.index header]
          else
            values[header]
          end
        end
      puts fields
        merged << (CSV::Row.new orig.headers, fields)
      end
    end
  end

 

В моем варианте кода ошибка - получаю фаил с хидерами и с данными но тоже только один ряд, на пример если должно получится:
---url1------------|------url2------------|---date visited----|------time visited---|----ip from-----|-links cliked 
--myhome.com--|--friendhome.com--|---16/02/10-------|--------12.45--------|-192.168.1.1-- |--link1-------
--someone.com--|--bigimot.com------|----13/10/14------|--------11.15--------|--199.168.1.1--|--link2----

То я получаю только:
---url1------------|------url2------------|---date visited----|------time visited---|----ip from-----|-links cliked 
--myhome.com--|--friendhome.com--|---16/02/10-------|--------12.45--------|-192.168.1.1-- |--link1-------

 

 

 

 

 

 

Кто может помогите пожалуйста!!!
Благодарю всех за помощь!!!


  • 0

#2 Akhenaton

Akhenaton
  • Постоялец
  • 8 041 сообщений

Отправлено 31 Август 2014 - 21:34

На скорую руку такая идея -> group_by { |entry| [entry.url1, entry.url2] }, а затем делаешь map на полученные элементы в группах и делаешь merge итеративно, как хэш.

Но это, вообще, первая намётка, что в голову пришло. Я точно не помню, как делается multi group. Могу потом уточнить =)  


  • 0

#3 arthur1981

arthur1981
  • Новобранец
  • 14 сообщений

Отправлено 01 Сентябрь 2014 - 00:07

На скорую руку такая идея -> group_by { |entry| [entry.url1, entry.url2] }, а затем делаешь map на полученные элементы в группах и делаешь merge итеративно, как хэш.

Но это, вообще, первая намётка, что в голову пришло. Я точно не помню, как делается multi group. Могу потом уточнить =)  

Спасибо большое за помощь!!!  Если можете, вышлите свой вариант кода пожалуйса, ато уже все перепробывал что мог!


  • 0

#4 Akhenaton

Akhenaton
  • Постоялец
  • 8 041 сообщений

Отправлено 01 Сентябрь 2014 - 08:54

Спасибо большое за помощь!!!  Если можете, вышлите свой вариант кода пожалуйса, ато уже все перепробывал что мог!

 

На совсем скорую руку набросал коду:

 

c.group_by {|e| [ e[:url1], e[:url2] ] }.map {|_,v| v = v.map {|a| a.reject {|_,y| y.blank?} };  v.reduce({}, :merge)}

 

Где c - массив, который содержит все rows. Т.е тебе из файла вначале надо все вычитать в один массив. 

Тут главная фишка в том, чтобы перед слиянием из слияемых хэшей выкинуть те значения, которые пустые. Иначе поведение может быть непредсказуемым при слиянии. 
Просто сказу сразу, что если ты делаешь слияние двух хешей, и у них в одном месте в одном ключе разные значение, то конечное значение будет равно последнему.

 

{:one =>1}.merge({:one => 2}) # => {:one => 2}

 

Вот )) Не уверен, конечно, что идеально работает, но поидее должно )) Если че - потом еще взгляну, допилю, если надо.


Сообщение изменено: Akhenaton (01 Сентябрь 2014 - 08:58 )

  • 0

#5 arthur1981

arthur1981
  • Новобранец
  • 14 сообщений

Отправлено 01 Сентябрь 2014 - 10:50   Лучший Ответ

На совсем скорую руку набросал коду:

 

c.group_by {|e| [ e[:url1], e[:url2] ] }.map {|_,v| v = v.map {|a| a.reject {|_,y| y.blank?} };  v.reduce({}, :merge)}

 

Где c - массив, который содержит все rows. Т.е тебе из файла вначале надо все вычитать в один массив. 

Тут главная фишка в том, чтобы перед слиянием из слияемых хэшей выкинуть те значения, которые пустые. Иначе поведение может быть непредсказуемым при слиянии. 
Просто сказу сразу, что если ты делаешь слияние двух хешей, и у них в одном месте в одном ключе разные значение, то конечное значение будет равно последнему.

 

{:one =>1}.merge({:one => 2}) # => {:one => 2}

 

Вот )) Не уверен, конечно, что идеально работает, но поидее должно )) Если че - потом еще взгляну, допилю, если надо.

Большое спасибо за помощь!


  • 0

#6 Akhenaton

Akhenaton
  • Постоялец
  • 8 041 сообщений

Отправлено 01 Сентябрь 2014 - 17:15

Работает-то хоть? )) Я написал это не тестируя ничего ) 


  • 0

#7 arthur1981

arthur1981
  • Новобранец
  • 14 сообщений

Отправлено 02 Сентябрь 2014 - 10:28

 если удаляю хидеры тогда работает


 если удаляю хидеры тогда работает

у меня в начальном файле с верху идет сначала два ряда хидеров, в этом проблема сейчас


  • 0

#8 Akhenaton

Akhenaton
  • Постоялец
  • 8 041 сообщений

Отправлено 02 Сентябрь 2014 - 11:31

Ну вот это и есть тот случай неопределенности при слиянии. Поэтому просто убирай. Все равно это не данные, а шум лишний.

 

А еще у тебя может быть такая проблема, что пара <url1, url2> будет не равна <url2, url1> 

Например, --myhome.com--|--friendhome.com-- и --friendhome.com--|--myhome.com-- 

Группировка по ним даст два сета, вместо одного. 


  • 0

#9 arthur1981

arthur1981
  • Новобранец
  • 14 сообщений

Отправлено 02 Сентябрь 2014 - 12:11

Ну вот это и есть тот случай неопределенности при слиянии. Поэтому просто убирай. Все равно это не данные, а шум лишний.

 

А еще у тебя может быть такая проблема, что пара <url1, url2> будет не равна <url2, url1> 

Например, --myhome.com--|--friendhome.com-- и --friendhome.com--|--myhome.com-- 

Группировка по ним даст два сета, вместо одного. 

Да такая проблема уже есть, думаю ее так решить row[0], row[1] = row[1], row[0]


  • 0





Читать еще на тему: Ruby, CSV