-1

我有以下格式的 CSV:

name,contacts.0.phone_no,contacts.1.phone_no,codes.0,codes.1
YK,1234,4567,AB001,AK002

如您所见,这是一个嵌套结构。CSV 可能包含多行。我想把它转换成这样的哈希数组:

[
  {
    name: 'YK',
    contacts: [
        {
            phone_no: '1234'
        },
        {
            phone_no: '4567'
        }
    ],
    codes: ['AB001', 'AK002']
  }
]

该结构使用给定格式的数字来表示数组。数组内部可以有哈希值。在 Ruby 中是否有一种简单的方法可以做到这一点?

CSV 标头是动态的。它可以改变。我将不得不根据 CSV 文件动态创建散列。

有一个名为csvtojson的类似节点库可以为 JavaScript 执行此操作。

4

2 回答 2

1

让我们首先构建一个 CSV 文件。

str = <<~END
name,contacts.0.phone_no,contacts.1.phone_no,codes.0,IQ,codes.1
YK,1234,4567,AB001,173,AK002
ER,4321,7654,BA001,81,KA002
END

FName = 't.csv'

File.write(FName, str)
  #=> 121

我构造了一个辅助方法来构造一个模式,该模式将用于将 CSV 文件的每一行(在第一行之后,包含标题)转换为所需数组的元素(哈希)。

require 'csv'

def construct_pattern(csv)
  csv.headers.group_by { |col| col[/[^.]+/] }.
      transform_values do |arr|
        case arr.first.count('.')
        when 0
          arr.first
        when 1
          arr
        else 
          key = arr.first[/(?<=\d\.).*/]
          arr.map { |v| { key=>v } }
        end
      end
end

在下面的代码中,对于正在考虑的示例:

construct_pattern(csv)
  #=> {"name"=>"name",
  #    "contacts"=>[{"phone_no"=>"contacts.0.phone_no"},
  #                 {"phone_no"=>"contacts.1.phone_no"}],
  #    "codes"=>["codes.0", "codes.1"],
  #    "IQ"=>"IQ"}

通过附加if pattern.empty?上面的表达式,我们确保模式只构造一次。

我们现在可以构造所需的数组。

pattern = {}
CSV.foreach(FName, headers: true).map do |csv|
  pattern = construct_pattern(csv) if pattern.empty?
  pattern.each_with_object({}) do |(k,v),h|
    h[k] =
    case v
    when Array
      case v.first
      when Hash
        v.map { |g| g.transform_values { |s| csv[s] } }
      else
        v.map { |s| csv[s] }
      end
    else
      csv[v]
    end
  end
end
  #=> [{"name"=>"YK",
  #     "contacts"=>[{"phone_no"=>"1234"}, {"phone_no"=>"4567"}],
  #     "codes"=>["AB001", "AK002"],
  #     "IQ"=>"173"},
  #    {"name"=>"ER",
  #     "contacts"=>[{"phone_no"=>"4321"}, {"phone_no"=>"7654"}],
  #     "codes"=>["BA001", "KA002"],
  #     "IQ"=>"81"}] 

我使用的 CSV 方法记录在CSV中。另请参见Enumerable#group_byHash#transform_values

于 2019-10-28T19:30:47.307 回答
1

只需逐行阅读并解析它。下面arr代码中的变量将保存您需要的哈希数组

arr = []

File.readlines('README.md').drop(1).each do |line|
  fields = line.split(',').map(&:strip)

  hash = { name: fields[0], contacts: [fields[1], fields[2]], address: [fields[3], fields[4]] }
  arr.push(hash)
end
于 2019-10-28T12:00:48.457 回答