What is the right way to iterate through an array in Ruby?
This will iterate through all the elements:
array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }
# Output:
1
2
3
4
5
6
This will iterate through all the elements giving you the value and the index:
array = ["A", "B", "C"]
array.each_with_index {|val, index| puts "#{val} => #{index}" }
# Output:
A => 0
B => 1
C => 2
I'm not quite sure from your question which one you are looking for.
Is there an elegant way to iterate through an array and stop before the end?
What you are effectively doing is, first map
ping the elements of the Array
with a transformation, and then find
ing the first transformed element that satisfies some predicate.
We can express this like this (I'm re-using the definitions from @iGian's answer, but with an added side-effect so that you can observe the evaluation of the transformation operation):
def complex_stuff_with(e)
p "#{__callee__}(#{e.inspect})"
e**2
end
ary = [1, 2, 3, 2]
x_i_want = 4
ary
.map(&method(:complex_stuff_with))
.find(&x_i_want.method(:==))
# "complex_stuff_with(1)"
# "complex_stuff_with(2)"
# "complex_stuff_with(3)"
# "complex_stuff_with(2)"
#=> 4
This gives us the correct result but not the correct side-effects. You want the operation to be lazy
. Well, that's easy, we just need to convert the Array
to a lazy enumerator first:
ary
.lazy
.map(&method(:complex_stuff_with))
.find(&x_i_want.method(:==))
# "complex_stuff_with(1)"
# "complex_stuff_with(2)"
#=> 4
Or, using the definitions from your question:
array
.lazy
.map(&method(:complex_stuff_with))
.find(&:is_the_one_i_want?)
How can I iterate over an array of hashes and form new one
You can use map
which will iterate through the elements in an array and return a new array:
companies["items"].map do |c|
{
title: c['title'],
company_number: c['company_number']
}
end
=> [
{:title=>"First company", :company_number=>"10471071323"},
{:title=>"Second company", :company_number=>"1047107132"}
]
I need to iterate over an array and add elements in certain conditions
As a general rule, there is never a need to use loops in Ruby. You should always prefer to use higher-level iteration constructs. Also, never mutate a data structure while you are iterating over it. (Of course, if you know me, you know I am a fan of functional programming, and thus I would say, never mutate a data structure at all.)
Here's a couple of ideas how I would solve the problem:
indices = vet.
each_cons(2).
with_index.
map {|pair, index| if pair == [2, 2] then index end }.
compact
indices.reverse_each {|index| vet.insert(index + 1, -1) }
This actually mutates vet
and does so while iterating over it, which are both things I said above not to do. However, it does so in a safe manner, by extending the array from the back so that the indices yet to be processed don't shift around.
It also uses the rather "low-level" Enumerable#reverse_each
iterator, which is actually not much better than a loop.
Here is a version that does not mutate vet
and uses higher-level iterators:
([nil] + vet).
each_cons(2).
flat_map {|a, b| if [a, b] == [2, 2] then [-1, b] else b end }
Note that even without the bug, the code in your question is still wrong: you say you want to place a -1
between all the 2
s of the array. However, your code places a -1
after every 2
.
So, for example, if the input is [2]
, your code will produce [2, -1]
instead of the correct result, which is [2]
.
How to iterate through an array starting from the last element? (Ruby)
array.reverse.each { |x| puts x }
What's the 'Ruby way' to iterate over two arrays at once
>> @budget = [ 100, 150, 25, 105 ]
=> [100, 150, 25, 105]
>> @actual = [ 120, 100, 50, 100 ]
=> [120, 100, 50, 100]
>> @budget.zip @actual
=> [[100, 120], [150, 100], [25, 50], [105, 100]]
>> @budget.zip(@actual).each do |budget, actual|
?> puts budget
>> puts actual
>> end
100
120
150
100
25
50
105
100
=> [[100, 120], [150, 100], [25, 50], [105, 100]]
How to iterate over an array of hashes in Ruby and return all of the values of a specific key in a string
You can use Enumerable#map for this:
p foods.map { |f| f[:name] }
The code you tried to use did not produce any output or create any objects, and it was not necessary to use a second loop to access a single element of a hash.
A better method to iterate over an array and object attributes?
def mymethod
params = {'foo'=>:blue,'bar'=>:red,'jaa'=>:yellow}
params.each do |k,v|
my_object.send(v).push(collect_method(k))
end
end
where collect_method is another method in Myobject class
Loop through array of hash with a key containing an array
A simple approach is to just iterate both, array and product ids:
array.each do |hash|
hash[:product_ids].each do |product_id|
StoreExcludedProduct.create(
store_id: hash[:store_id],
product_id: product_id
)
end
end
You can wrap the above code in a transaction
call to avoid having separate transactions for each create
, i.e.:
StoreExcludedProduct.transaction do
array.each do |hash|
# ...
end
end
This also makes your code run in an all-or-nothing way.
Another option is insert_all
which can insert multiple records at once. In order to use it, you have to first build an array of attributes for each record, i.e. you have to split the product ids. This works a lot like the above code:
attributes = array.flat_map do |hash|
hash[:products_ids].map do |product_id|
{ store_id: hash[:store_id], product_id: product_id }
end
end
#=> [
# {:store_id=>5, :product_id=>1},
# {:store_id=>5, :product_id=>4},
# {:store_id=>5, :product_id=>19},
# {:store_id=>5, :product_id=>40},
# {:store_id=>13, :product_id=>2},
# #...
# ]
which can be passed to insert_all
:
StoreExcludedProduct.insert_all(attributes)
Note that this performs a raw SQL query without instantiating any models or running any callbacks or validations.
Related Topics
Why Do Two Strings Separated by Space Concatenate in Ruby
Ruby: Problem Installing Eventmachine Under Windows 7
Idiomatic Object Creation in Ruby
Controller Spec Unknown Keyword: Id
How to Format the Value Shown in a Rails Edit Field
Match a String Against Multiple Patterns
Find Number of Months Between Two Dates in Ruby on Rails
How to Use "Rvm --Default" on Macosx
Unable to Obtain Stable Firefox Connection in 60 Seconds (127.0.0.1:7055)
Undefined Method Attr_Accessible
Looking For Suggestions For Building a Secure Rest API Within Ruby on Rails
Gem Eventmachine Fatal Error: 'Openssl/Ssl.H' File Not Found
Why Can't I Install Rails on Lion Using Rvm
In Ruby, How Does Coerce() Actually Work