1

I don't understand this, as far as I can tell these two snippets of code should do the same thing. But they aren't. Can someone shine some light as to why this is happening?

I am simply trying to find the total files inside a folder. This count should include all nested folders files also.

Here is my test case:

A - File 1 - File 2 - File 3 -- B - File 4 -- D - File 5 -- C - File 6

When I run this snippet,

def get_all_files(myfolder, filearray = Array.new)
    filearray += myfolder.myfiles.pluck(:id)
    myfolder.children.each { |c| get_all_files(c, filearray) }
    return filearray
end

It returns only 3 files. (Only A's file.)

When I run this snippet,

def get_all_files(myfolder, filearray = Array.new)
    myfolder.myfiles.pluck(:id).each do |id|
      filearray.push(id)
    end
    myfolder.children.each { |c| get_all_files(c, filearray) }
    return filearray
end

It runs the proper number of files, which is 6. I thought both .push and + are just normal Ruby array methods. So why does this happen?

4
  • No idea if this solves your problem/question, but the typical way to push onto an array is using the shovel << operator. filearray << myfolder.myfiles.pluck(:id) Commented Jun 5, 2015 at 17:05
  • Try file_array += myfolder.children.each { |c| get_all_files(c, filearray) }. You can also delete return file_array. Commented Jun 5, 2015 at 17:07
  • 1
    Yeah that's a mistake when I copied it to Stackoverflow. I named it better here (had it named recur when testing). Commented Jun 5, 2015 at 17:09
  • @JeffPrice Okay. Let me check if that solves it. Commented Jun 5, 2015 at 17:11

2 Answers 2

2

+= returns a new instance of the array. Your recursion is not doing anything with the return value so that new instance dies at the end of the method.

When you do push or <<, you are operating on the original instance of the array. That is why you see different behavior.

Sign up to request clarification or add additional context in comments.

Comments

1

Try this:

def get_all_files(myfolder)
    folder_files   = myfolder.myfiles.pluck(:id))
    children_files = myfolder.children.each_with_object([]) do |c,a|
      a += get_all_files(c, filearray))
    end if my_folder.children.any?
    folder_files + children_files
end

The problem is with your line:

myfolder.children.each { |c| get_all_files(c, filearray) }

which obtains children's files, but does nothing with them.

To see what's happening, consider the following:

def a(h, x=[])
  puts h[:name]
  puts "x=#{x}, x.object_id 1 = #{x.object_id}"
  x += [1]
  puts "x=#{x}, x.object_id 2 = #{x.object_id}"
  h[:children].each do |g| 
    a(g,x)
    puts h[:name]
    puts  "x=#{x}, x.object_id 3 = #{x.object_id}"
  end
  x
end

h = { name: "Bob",
      children: [
        { name: "Marsha", children: [] },
        { name: "Bippy" , children: [] }
      ]
    }

a(h)
  # Bob
  # x=[],         x.object_id 1 = 70286033312340
  # x=[1],        x.object_id 2 = 70286033311720
  #   Marsha
  #     x=[1],    x.object_id 1 = 70286033311720
  #     x=[1, 1], x.object_id 2 = 70286033311280
  # Bob
  # x=[1],        x.object_id 3 = 70286033311720
  #   Bippy
  #     x=[1],    x.object_id 1 = 70286033311720
  #     x=[1, 1], x.object_id 2 = 70286033310660
  # Bob
  # x=[1],        x.object_id 3 = 70286033311720
  # => [1] 

2 Comments

I liked your other answer better.
@Dave, for me, a tough choice. Still disturbed?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.