Andy Harvey
Andy Harvey

Reputation: 12663

How to loop within a method?

I am trying to add a loop within a method in a Rails app. It looks something like this

Parent.do_something(
  attribute: "a string",
  parameter: "a string",
  child[0]: "child_url"
  )

Sometimes a parent will have no children. Sometime a parent will have x children. How can I create a loop within a function that will cycle through all these children.

I want something like

i=0
children=Child.all
Parent.do_something(
  attribute: "a string",
  parameter: "a string",
  for child in children
   child[i]: "child_url"
   i= i + 1
  end
  )

That will generate

Parent.do_something(
      attribute: "a string",
      parameter: "a string",
      child[0]: "child_0_url",
      child[1]: "child_1_url",
      child[2]: "child_2_url"
      )

If I have not explained this problem very clearly, I will update my question based on comments.

Upvotes: 0

Views: 138

Answers (5)

davogones
davogones

Reputation: 7409

If you're trying to pass a variable number of arguments to a method, you're probably looking for the splat (*) operator.

Upvotes: 1

MMeersseman
MMeersseman

Reputation: 2631

If you want the urls like you typed them, try this:

children = Child.all
Parent.do_something(
  attribute: "a string",
  parameter: "a string",
  child: something
)

def something
  child = []
  children.length.times { |index| child << "child_#{index}_url"}
  return child
end

You could also replace children.length with Child.count if you don't need the children elsewhere, but I'm assuming you do.

Edit: I think this may be more what you are looking for:

children = Child.all
Parent.do_something(
  attribute: "a string",
  parameter: "a string",
  child: children.each_with_index.map { |child,i| "child_#{i}_#{child.url}"}
)

This takes advantage of the fact that each_with_index returns an Enumerator if no block is given.

Upvotes: 0

Pavling
Pavling

Reputation: 3963

As others have suggested, it might be better to redesign your method to expect an array of children, rather than lots of single parameters :

Parent.do_something(
  attribute: "a string",
  parameter: "a string",
  children: ["child_0_url", "child_1_url", "child_2_url"]
  )

But if you have to do it the way you said (for instance, if you're constrained by someone else's poor API):

children = Child.all
Parent.do_something(
  {attribute: "a string",
   parameter: "a string"}.merge Hash[*(children.each_with_index.map { |child, i| ["child[#{i}]", child.url] }.flatten)]
)

Ugly, huh?

As the saying goes; if it's hard to do, you're probably doing it wrong. The nice flat map of Ismael Abreu's answer is much prettier.

Upvotes: 1

Ismael
Ismael

Reputation: 16730

You might just want to do this:

children = Child.all
Parent.do_something(
  attribute: "a string",
  parameter: "a string",
  child: children.map { |child| child.url }
)

Upvotes: 2

mind.blank
mind.blank

Reputation: 4880

Might be easier to extract that part into a different method:

Parent.do_something(
  attribute: "a string",
  parameter: "a string",
  children: children_method
)

def children_method
  Parent.children.map do |child|
     # whatever needs to be done
  end
end

Upvotes: 1

Related Questions