¿Una forma rápida de generar una matriz de hashes en Ruby?

I'm trying to build an array of folders with the files contained in each. So if your directory structure is:

DirA
  |- FileA
  \- FileB
DirB
  \- FileC

And I'm given this array:

files = ["DirA/FileA", "DirA/FileB", "DirB/FileC"]

I'm trying to build a hash like this

{DirA => [FileA, FileB], DirB => [FileC]}

Right now I'm doing it in what I think is a fairly non-Rubyish way (assume String has a method defined which fetches the parent directory):

h = {}
files.each do |f|
    parent = f.getParentDir
    if not h[parent] then h[parent] = [] end
    h[parent].push f
end

Is there are more elegant way?

preguntado el 25 de agosto de 12 a las 04:08

You only care about files directly contained in directories, not nested files/directories? -

I'm using a list of files provided to me, and I'm interested in referring to them by their parent directories so I can check they all exist. -

if not h[parent] then h[parent] = [] end puede ser reemplazado con h[parent] = [] unless h[parent] or the even rubier h[parent] ||= []. -

4 Respuestas

yo lo haría

h = {}
files.each do |f|
    (h[f.getParentDir] ||= []) << f
end

Respondido 25 ago 12, 04:08

Nice. Lots more good answers but this was the shortest, and it uses the method I had defined on String. In my actual use case getParentPath is a method I define on NSURL but I was trying to simplify things for you guys. Cheers! - Chris Adams

I think you need this hash (with array)

{DirA => [FileA, FileB], DirB => [FileC]}

files.each_with_object(Hash.new{|h,k|h[k]=[]}) do |m,res|
  k,v = m.split('/')
  res[k] << v
end

Respondido 25 ago 12, 04:08

files = ["DirA/FileA", "DirA/FileB", "DirB/FileC"]

files.each_with_object(Hash.new { |h,k| h[k] = [] }) do |path, hash|
  parent, file = path.split('/', 2)
  hash[parent] << file
end

#=> {"DirA"=>["FileA", "FileB"], "DirB"=>["FileC"]}

Respondido 25 ago 12, 04:08

Esto haría el truco:

result = files.group_by { |i| i.split("/").first }
result.each_key { |k| result[k] = result[k].map { |f| f.split("/").last } }

If you're into brevity, you can replace the second line with

result.merge!(result) { |k, v| v.map { |f| f.split("/").last } }

Respondido 25 ago 12, 04:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.