a = [1, 2, 3]
a.each do |x| x+=10 end
After this operation array a is still [1, 2, 3]. How to convert it into [11, 12, 13]?
Use the collect! method:
a = [1, 2, 3]
a.collect!{ |x| x + 10 }
There are two general classes of solutions:
Imperative object-mutating code
a.map! { |x| x + 10 }
An almost functional solution
a = a.map { |x| x + 10 }
Both techniques have their place.
I like the aliased name "map" myself. It has less characters.
The difference with these methods as compared to what you've done is two fold. One is that you have to use a method that modifies the initial array (typically these are the bang methods, or the methods which have a name ending in a ! (map!, collect!, ...) The second thing is that a.each is the method typically used for just going through the array to use the individual elements. Map or Collect methods return an array containing a return from each iteration of the block.
Hence, you could have done the following:
a = [1,2,3]
b = []
a.each do |x|
b << x+10
end
or you could use the map or collect method as demonstrated by dmarko or as here:
a = [1,2,3]
a = a.map {|x| x+10}
Related
I try to clean my Code. The first Version uses each_with_index. In the second version I tried to compact the code with the Enumerable.inject_with_index-construct, that I found here.
It works now, but seems to me as obscure as the first code.
Add even worse I don't understand the brackets around element,index in
.. .inject(groups) do |group_container, (element,index)|
but they are necessary
What is the use of these brackets?
How can I make the code clear and readable?
FIRST VERSION -- WITH "each_with_index"
class Array
# splits as good as possible to groups of same size
# elements are sorted. I.e. low elements go to the first group,
# and high elements to the last group
#
# the default for number_of_groups is 4
# because the intended use case is
# splitting statistic data in 4 quartiles
#
# a = [1, 8, 7, 5, 4, 2, 3, 8]
# a.sorted_in_groups(3) # => [[1, 2, 3], [4, 5, 7], [8, 8]]
#
# b = [[7, 8, 9], [4, 5, 7], [2, 8]]
# b.sorted_in_groups(2) {|sub_ary| sub_ary.sum } # => [ [[2, 8], [4, 5, 7]], [[7, 8, 9]] ]
def sorted_in_groups(number_of_groups = 4)
groups = Array.new(number_of_groups) { Array.new }
return groups if size == 0
average_group_size = size.to_f / number_of_groups.to_f
sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort
sorted.each_with_index do |element, index|
group_number = (index.to_f / average_group_size).floor
groups[group_number] << element
end
groups
end
end
SECOND VERSION -- WITH "inject" AND index
class Array
def sorted_in_groups(number_of_groups = 4)
groups = Array.new(number_of_groups) { Array.new }
return groups if size == 0
average_group_size = size.to_f / number_of_groups.to_f
sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort
sorted.each_with_index.inject(groups) do |group_container, (element,index)|
group_number = (index.to_f / average_group_size).floor
group_container[group_number] << element
group_container
end
end
end
What is the use of these brackets?
It's a very nice feature of ruby. I call it "destructuring array assignment", but it probably has an official name too.
Here's how it works. Let's say you have an array
arr = [1, 2, 3]
Then you assign this array to a list of names, like this:
a, b, c = arr
a # => 1
b # => 2
c # => 3
You see, the array was "destructured" into its individual elements. Now, to the each_with_index. As you know, it's like a regular each, but also returns an index. inject doesn't care about all this, it takes input elements and passes them to its block as is. If input element is an array (elem/index pair from each_with_index), then we can either take it apart in the block body
sorted.each_with_index.inject(groups) do |group_container, pair|
element, index = pair
# or
# element = pair[0]
# index = pair[1]
# rest of your code
end
Or destructure that array right in the block signature. Parentheses there are necessary to give ruby a hint that this is a single parameter that needs to be split in several.
Hope this helps.
lines = %w(a b c)
indexes = lines.each_with_index.inject([]) do |acc, (el, ind)|
acc << ind - 1 if el == "b"
acc
end
indexes # => [0]
What is the use of these brackets?
To understand the brackets, first you need to understand how destruction works in ruby. The simplest example I can think of this this:
1.8.7 :001 > [[1,3],[2,4]].each do |a,b|
1.8.7 :002 > puts a, b
1.8.7 :003?> end
1
3
2
4
You should know how each function works, and that the block receives one parameter. So what happens when you pass two parameters? It takes the first element [1,3] and try to split (destruct) it in two, and the result is a=1 and b=3.
Now, inject takes two arguments in the block parameter, so it is usually looks like |a,b|. So passing a parameter like |group_container, (element,index)| we are in fact taking the first one as any other, and destructing the second in two others (so, if the second parameter is [1,3], element=1 and index=3). The parenthesis are needed because if we used |group_container, element, index| we would never know if we are destructing the first or the second parameter, so the parenthesis there works as disambiguation.
9In fact, things works a bit different in the bottom end, but lets hide this for this given question.)
Seems like there already some answers given with good explanation. I want to add some information regards the clear and readable.
Instead of the solution you chose, it is also a possibility to extend Enumerable and add this functionality.
module Enumerable
# The block parameter is not needed but creates more readable code.
def inject_with_index(memo = self.first, &block)
skip = memo.equal?(self.first)
index = 0
self.each_entry do |entry|
if skip
skip = false
else
memo = yield(memo, index, entry)
end
index += 1
end
memo
end
end
This way you can call inject_with_index like so:
# m = memo, i = index, e = entry
(1..3).inject_with_index(0) do |m, i, e|
puts "m: #{m}, i: #{i}, e: #{e}"
m + i + e
end
#=> 9
If you not pass an initial value the first element will be used, thus not executing the block for the first element.
In case, someone is here from 2013+ year, you have each_with_object and with_index for your needs:
records.each_with_object({}).with_index do |(record, memo), index|
memo[record.uid] = "#{index} in collection}"
end
What is the clearest and most efficient way to add all the elements in 2D array by their position in Ruby. Example:
2darray = [[1,2,3],[1,2,3]]
result = [2,4,6]
I have the following code
def sum_elements_by_position(array)
total_elements = array.length
result = []
for i in 0...array.first.length
n = 0
array.each { |subarray| n += subarray[i] }
result << n
end
result
end
Assumptions: All primary elements are of the same length
For bonus points it would be great to see a solution that works primary elements of an arbitrary length
You can zip the first row with the rest of them and then do the sum:
def sum_elements_by_position(array)
array[0].zip(*array[1..-1]).map do |col|
col.inject(:+)
end
end
Here's a solution addressing when the rows aren't the same length.
def sum_cols arr
arr.reduce( [] ) do |res,row|
row.each_with_index { |e,i| res[i] ||= 0; res[i] += e }
res
end
end
irb> sum_cols [ [0,1,2], [3,4], [5,6,7,8] ]
=> [8, 11, 9, 8]
#oldergod suggested using zip based on the longest row, but finding the longest row and rejecting nils has a cost. I benchmarked the following against the above method using the example array above and found the reduce+each_with_index method more than 30% faster:
def sum_cols_using_zip arr
max_len = arr.map(&:size).max
([0] * max_len).zip(*arr).map do |col|
col.compact.inject(:+)
end
end
I'd do this:
a.transpose.map {|x| x.reduce(:+)}
Clean, simple, flexible. The .transpose turns this
[[1,2,3,4],[2,3,4,5],[3,4,5,6]]
into this
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
and then .map applies .reduce to each subarray. And .reduce, in turn, aggregates the subvalues by adding them. Or, more precisely, by applying the + method to them.
I highly recommend reading the doc for these functions until you fully understand this example, as it's a pretty good succinct demonstration of how to think in a Rubyish way!
I have two list
a = [1, 2, 3, 4, 1, 2]
b = a.uniq # b = [1, 2, 3, 4]
c = a - b
the result gets c = []
I expects the result c = [1, 2]
How can I get this?
This is the expected result according to the Array documentation.
If you want to get the values which have multiple occurrences within the array you could something like:
a.uniq.keep_if {|v| a.count(v) > 1}
If Ruby 1.8 is used, keep_if is not available, so you need to use something like:
a.uniq.delete_if {|v| a.count(v) == 1}
(Both methods keep a intact)
If you want to get elements from array which appears more than once you can use following code:
a.delete_if { |x| a.count(x) <= 1 }.uniq
If you want to perform sub operation on two arrays based on elements position you can use following code:
i = 0
a.drop_while { |x| b[i+=1] == x }.uniq
From the documentation
Array Difference—Returns a new array that is a copy of the original array, removing any items that also appear in other_ary. (If you need set-like behavior, see the library class Set.)
You are looking for all duplicates or the opposite of uniq? Maybe this link will help: Identify duplicates in an array
Looks like you are trying to find elements that have duplicates. Here are several ways, pick the one you like:
a.select { |x| a.count(x) > 1 }.uniq
a.sort.each_cons(2).map { |x,y| x if x == y }.compact
a.group_by{|x|x}.values.select{|k|k.size>1}.map(&:first)
(1..4).collect do |x|
next if x == 3
x + 1
end # => [2, 3, nil, 5]
# desired => [2, 3, 5]
If the condition for next is met, collect puts nil in the array, whereas what I'm trying to do is put no element in the returned array if the condition is met. Is this possible without calling delete_if { |x| x == nil } on the returned array?
My code excerpt is heavily abstracted, so looking for a general solution to the problem.
There is method Enumerable#reject which serves just the purpose:
(1..4).reject{|x| x == 3}.collect{|x| x + 1}
The practice of directly using an output of one method as an input of another is called method chaining and is very common in Ruby.
BTW, map (or collect) is used for direct mapping of input enumerable to the output one. If you need to output different number of elements, chances are that you need another method of Enumerable.
Edit: If you are bothered by the fact that some of the elements are iterated twice, you can use less elegant solution based on inject (or its similar method named each_with_object):
(1..4).each_with_object([]){|x,a| a << x + 1 unless x == 3}
I would simply call .compact on the resultant array, which removes any instances of nil in an array. If you'd like it to modify the existing array (no reason not to), use .compact!:
(1..4).collect do |x|
next if x == 3
x
end.compact!
In Ruby 2.7+, it’s possible to use filter_map for this exact purpose. From the docs:
Returns an array containing truthy elements returned by the block.
(0..9).filter_map {|i| i * 2 if i.even? } #=> [0, 4, 8, 12, 16]
{foo: 0, bar: 1, baz: 2}.filter_map {|key, value| key if value.even? } #=> [:foo, :baz]
For the example in the question: (1..4).filter_map { |x| x + 1 unless x == 3 }.
See this post for comparison with alternative methods, including benchmarks.
just a suggestion, why don't you do it this way:
result = []
(1..4).each do |x|
next if x == 3
result << x
end
result # => [1, 2, 4]
in that way you saved another iteration to remove nil elements from the array. hope it helps =)
i would suggest to use:
(1..4).to_a.delete_if {|x| x == 3}
instead of the collect + next statement.
You could pull the decision-making into a helper method, and use it via Enumerable#reduce:
def potentially_keep(list, i)
if i === 3
list
else
list.push i
end
end
# => :potentially_keep
(1..4).reduce([]) { |memo, i| potentially_keep(memo, i) }
# => [1, 2, 4]
I have an array that I want to iterate over and delete some of the elements. This doesn't work:
a = [1, 2, 3, 4, 5]
a.each do |x|
next if x < 3
a.delete x
# do something with x
end
a #=> [1, 2, 4]
I want a to be [1, 2]. How can I get around this?
a.delete_if { |x| x >= 3 }
See method documentation here
Update:
You can handle x in the block:
a.delete_if do |element|
if element >= 3
do_something_with(element)
true # Make sure the if statement returns true, so it gets marked for deletion
end
end
You don't have to delete from the array, you can filter it so:
a = [1, 2, 3, 4, 5]
b = a.select {|x| x < 3}
puts b.inspect # => [1,2]
b.each {|i| puts i} # do something to each here
I asked this question not long ago.
Deleting While Iterating in Ruby?
It's not working because Ruby exits the .each loop when attempting to delete something. If you simply want to delete things from the array, delete_if will work, but if you want more control, the solution I have in that thread works, though it's kind of ugly.
Another way to do it is using reject!, which is arguably clearer since it has a ! which means "this will change the array". The only difference is that reject! will return nil if no changes were made.
a.delete_if {|x| x >= 3 }
or
a.reject! {|x| x >= 3 }
will both work fine.