Add two arrays into another array [duplicate] - ruby

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
ruby: sum corresponding members of two arrays
So I'm struggling to do this.
I am trying to add two arrays and put the results into a third one. Now I want to be able to do this through a function though.
So I have Array_One, Array_Two, and Array_Three.
I would want to call the "compare function" to one and two and make Three that length and then if they the lengths matched I would want to add One and Two and put the results in three.
Could this be done in one function?
Better way to do this?
That's my thought process but I don't have the knowledge of Ruby to do this.
EDIT:
Sorry for the vagueness.
Array_One = [3,4]
Array_Two = [1,3]
Array_Three= []
I would want to pass One and Two through a function that compares the length and verifies they're the same length.
Then I would, in my mind, send it through a function that actually does the adding.
So in the end I would have Array_Three = [4,7]
Hope that helps.

Your question's description is little confusing, but I think this may be what you want:
def add_array(a,b)
a.zip(b).map{|pair| pair.reduce(&:+) }
end
irb> add_array([1,2,3],[4,5,6])
=> [5, 7, 9]
In addition it generalize to add multiple arrays quite easily:
def add_arrays(first_ary, *other_arys)
first_ary.zip(*other_arys).map{|column| column.reduce(&:+) }
end
irb> add_arrays([1,2,3],[4,5,6])
=> [5, 7, 9]
irb> add_arrays([1,2,3],[4,5,6],[7,8,9])
=> [12, 15, 18]

One possible method (without any checks) assuming I understood your question:
x = [1, 2, 3]
y = [4, 5, 6]
z = []
x.each_with_index do |v, i|
z << v + y[i]
end
Another method (still assuming I understood the question):
[x, y].transpose.map {|v| v.reduce(:+)}

If by add you mean element-wise addition than you can use:
def add(a, b)
a.zip(b).map &:sum
end
assuming your environment has sum defined.
Sum is an alias for reduce(&:+), which you can use instead.
zip is a function that takes two arrays and returns an array of arrays:
[a1, a2, a3, ..., an].zip [b1, b2, b3, ..., bm]
#=> [[a1, b1], [a2, b2], [a3, b3], ..., [an, bn]]
# assuming n <= m
We than take our array of arrays and sum all numbers in that array together and than collect the results with map.
map is a function that takes a block and produces an array:
[c1, c2, c3, ..., cn].map &block
# => [block.call(c1), block.call(c2), block.call(c3), ..., block.call(cn)]
So if we get example input say
a = [1, 2, 3]
b = [4, 2, 5]
a.zip(b) #=> [[1,4], [2,2], [3,5]]
a.zip(b).map(&:sum) #=> [[1,4].sum, [2,2].sum, [3,5].sum] #=> [5, 4, 8]
Now we can check that the same length by using an if condition:
def add(a, b)
a.zip(b).map &:sum if a.size == b.size
end

If you want to add two arrays, you can simply add them:
array1 = [1, 2, 3]
array2 = ['a', 'b', 'c']
if array1.length == array2.length
array3 = array1 + array2
end
# array3 = [1, 2, 3, 'a', 'b', 'c']

Related

How to insert an array in the middle of an array?

I have a Ruby array [1, 4]. I want to insert another array [2, 3] in the middle so that it becomes [1, 2, 3, 4]. I can achieve that with [1, 4].insert(1, [2, 3]).flatten, but is there a better way to do this?
You could do it the following way.
[1,4].insert(1,*[2,3])
The insert() method handles multiple parameters. Therefore you can convert your array to parameters with the splat operator *.
One form of the method Array#[]= takes two arguments, index and length. When the latter is zero and the rvalue is an array, the method inserts the elements of the rvalue into the receiver before the element at the given index (and returns the rvalue). Therefore, to insert the elements of:
b = [2,3]
into:
a = [1,4]
before the element at index 1 (4), we write:
a[1,0] = b
#=> [2,3]
a #=> [1,2,3,4]
Note:
a=[1,4]
a[0,0] = [2,3]
a #=> [2,3,1,4]
a=[1,4]
a[2,0] = [2,3]
a #=> [1,4,2,3]
a=[1,4]
a[4,0] = [2,3]
a #=> [1,4,nil,nil,2,3]]
which is why the insertion location is before the given index.
def insert_array receiver, pos, other
receiver.insert pos, *other
end
insert_array [1, 4], 1, [2, 3]
#⇒ [1, 2, 3, 4]
or, the above might be achieved by monkeypatching the Array class:
class Array
def insert_array pos, other
insert pos, *other
end
end
I believe, this is short enough notation to have any additional syntax sugar. BTW, flattening the result is not a good idea, since it will corrupt an input arrays, already having arrays inside:
[1, [4,5]].insert 1, *[2,3]
#⇒ [1, 2, 3, [4,5]]
but:
[1, [4,5]].insert(1, [2,3]).flatten
#⇒ [1, 2, 3, 4, 5]
My option without array#insert method
array = [1,2,3,6,7,8]
new_array = [4,5]
array[0...array.size/2] + new_array + array[array.size/2..-1]

How to preserve alphabetical order of keys when sorting a hash by the value

beginner here. My first question. Go easy on me.
Given the following hash:
pets_ages = {"Eric" => 6, "Harry" => 3, "Georgie" => 12, "Bogart" => 4, "Poly" => 4,
"Annie" => 1, "Dot" => 3}
and running the following method:
pets_ages.sort {|x, y| x[1] <=> y[1]}.to_h
the following is returned:
{
"Annie" => 1,
"Dot" => 3,
"Harry" => 3,
"Poly" => 4,
"Bogart" => 4,
"Eric" => 6,
"Georgie" => 12
}
You will notice the hash is nicely sorted by the value, as intended. What I'd like to change is the ordering of the keys, so that they remain alphabetical in the case of a tie. Notice "Dot" and "Harry" are correct in that regard, but for some reason "Poly" and "Bogart" are not. My theory is that it is automatically sorting the keys by length in the case of a tie, and not alphabetically. How can I change that?
In many languages, Hashes/Dicts aren't ordered, because of how the are implemented under the covers. Ruby 1.9+ is nice enough to guarantee ordering.
You can do this in a single pass - Ruby allows you to sort by arbitrary criteria.
# Given
pets_ages = {"Eric" => 6, "Harry" => 3, "Georgie" => 12, "Bogart" => 4, "Poly" => 4, "Annie" => 1, "Dot" => 3}
# Sort pets by the critera of "If names are equal, sort by name, else, sort by age"
pets_ages.sort {|(n1, a1), (n2, a2)| a1 == a2 ? n1 <=> n2 : a1 <=> a2 }.to_h
# => {"Annie"=>1, "Dot"=>3, "Harry"=>3, "Bogart"=>4, "Poly"=>4, "Eric"=>6, "Georgie"=>12}
Hash#sort will return an array of [k, v] pairs, but those k, v pairs can be sorted by any criteria you want in a single pass. Once we have the sorted pairs, we turn it back into a Hash with Array#to_h (Ruby 2.1+), or you can use Hash[sorted_result] in earlier versions, as Beartech points out.
You could get as complex as you want in the sort block; if you're familiar with Javascript sorting, Ruby actually works the same here. The <=> method returns -1, 0, or 1 depending on how the objects compare to each other. #sort just expects one of those return values, which tells it how the two given values relate to each other. You don't even have to use <=> at all if you don't want to - something like this is equivalent to the more compact form:
pets_ages.sort do |a, b|
if a[1] == b[1]
if a[0] > b[0]
1
elsif a[0] < b[0]
-1
else
0
end
else
if a[1] > b[1]
1
elsif a[1] < b[1]
-1
end
end
end
As you can see, as long as you always return something in the set (-1 0 1), your sort function can do whatever you want, so you can compose them however you'd like. However, such verbose forms are practically never necessary in Ruby, because of the super handy <=> operator!
As Stefan points out, though, you have a BIG shortcut here: Array#<=> is nice enough to compare each entry between the compared arrays. This means that we can do something like:
pets_ages.sort {|a, b| a.reverse <=> b.reverse }.to_h
This takes each [k, v] pair, reverses it into [v, k], and uses Array#<=> to compare it. Since you need to perform this same operation on each [k, v] pair compared, you can shortcut it even further with #sort_by
pets_ages.sort_by {|k, v| [v, k] }.to_h
What this does is for each hash entry, it passes the key and value to the block, and the return result of the block is what is used to compare this [k, v] pair to other entries. Since comparing [v, k] to another [v, k] pair will give us the result we want, we just return an array consisting of [v, k], which sort_by collects and sorts the original [k, v] pairs by.
As Philip pointed out, hashes were not meant to preserve order, though I think in the latest Ruby they might. But let's say they don't. Here's an array based solution that could then be re-hashed:
Edit here it is in a one-liner:
new_pets_ages = Hash[pets_ages.sort.sort_by {|a| a[1]}]
previous answer:
pets_ages = {"Eric" => 6, "Harry" => 3, "Georgie" => 12, "Bogart" => 4, "Poly" => 4,
"Annie" => 1, "Dot" => 3}
arr = pets_ages.sort
# [["Annie", 1], ["Bogart", 4], ["Dot", 3], ["Eric", 6], ["Georgie", 12],
# ["Harry", 3], ["Poly", 4]]
new_arr = arr.sort_by {|a| a[1]}
#[["Annie", 1], ["Dot", 3], ["Harry", 3], ["Bogart", 4], ["Poly", 4], ["Eric", 6],
# ["Georgie", 12]]
And finally to get a hash back:
h = Hash[new_arr]
#{"Annie"=>1, "Dot"=>3, "Harry"=>3, "Bogart"=>4, "Poly"=>4, "Eric"=>6,
# "Georgie"=>12}
So when we sort a hash, it gives us an array of arrays with the items sorted by the original keys. Then we sort that array of arrays by the second value of each, and since it's a lazy sort, it only shifts them if need be. Then we can send it back to a hash. I'm sure there's a trick way to do a two-pass sort in one line but this seems pretty simple.
As you already know few methods of ruby for sorting that you have used. So I would not explain it you in detail rather keep it very simple one liner for you. Here is your answer:
pets_ages.sort.sort_by{|pets| pets[1]}.to_h
Thanks

Adding two arrays in Ruby when the array length will always be the same

So, I need to add two arrays together to populate a third. EG
a = [1,2,3,4]
b = [3,4,5,6]
so that:
c = [4,6,8,10]
I read the answer given here: https://stackoverflow.com/questions/12584585/adding-two-ruby-arrays
but I'm using the codecademy labs ruby editor and it's not working there, plus the lengths of my arrays are ALWAYS going to be equal. Also, I don't have any idea what the method ".with_index" is or does and I don't understand why it's necessary to use ".to_i" when the value is already an integer.
It seems like this should be really simple?
a = [1,2,3,4]
b = [3,4,5,6]
a.zip(b).map { |i,j| i+j } # => [4, 6, 8, 10]
Here
a.zip(b) # => [[1, 3], [2, 4], [3, 5], [4, 6]]
and map converts each 2-tuple to the sum of its elements.
OPTION 1:
For a pure Ruby solution, try the transpose method:
a = [1,2,3,4]
b = [3,4,5,6]
c = [a, b].transpose.map{|x, y| x + y}
#=> [4,6,8,10]
OPTION 2:
If you're in a Rails environment, you can utilize Rails' sum method:
[a, b].transpose.map{|x| x.sum}
#=> [4,6,8,10]
EXPLANATION:
transpose works perfectly for your scenario, since it raises an IndexError if the sub-arrays don't have the same length. From the docs:
Assumes that self is an array of arrays and transposes the rows and columns.
If the length of the subarrays don’t match, an IndexError is raised.

Complex subsorts on an array of arrays

I wrote a quick method that confirms that data coming from a webpage is sorted correctly:
def subsort_columns(*columns)
columns.transpose.sort
end
Which worked for basic tests. Now, complex subsorts have been introduced, and I'm pretty certain I'll need to still use an array, since hashes can't be guaranteed to return in a specific order. The order of the input in this case represents subsort priority.
# `columns_sort_preferences` is an Array in the form of:
# [[sort_ascending_bool, column_data]]
# i.e.
# subsort_columns([true, column_name], [false, column_urgency], [true, column_date])
# Will sort on name ascending, then urgency descending, and finally date ascending.
def subsort_columns(*columns_sort_preferences)
end
This is where I'm stuck. I want to do this cleanly, but can't come up with anything but rolling out a loop for each subsort that occurs on any parent sort...but it sounds wrong.
Feel free to offer better suggestions, as I'm not tied to this implementation.
Here's some test data:
a = [1,1,1,2,2,3,3,3,3]
b = %w(a b c c b b a b c)
c = %w(x z z y x z z y z)
subsort_columns([true, a], [false, b], [false, c])
=> [[1, 'c', 'z'],
[1, 'b', 'z'],
[1, 'a', 'x'],
[2, 'c', 'y'],
[2, 'b', 'x'],
[3, 'c', 'z'],
[3, 'b', 'z'],
[3, 'b', 'y'],
[3, 'a', 'z']]
Update:
Marking for reopen because I've linked to this question in a comment above the function in our codebase that I provided as my own answer. Not to mention the help I got from an answer here that clearly displays the solution to my problem, whom I'd like to give a bounty to for giving me a tip in the right direction. Please don't delete this question, it is very helpful to me. If you disagree, at least leave a comment specifying what is unclear to you.
Use sort {|a, b| block} → new_ary:
a = [1,1,1,2,2,3,3,3,3]
b = %w(a b c c b b a b c)
c = %w(x z z y x z z y z)
sorted = [a, b, c].transpose.sort do |el1, el2|
[el1[0], el2[1], el2[2]] <=> [el2[0], el1[1], el1[2]]
end
Result:
[[1, "c", "z"],
[1, "b", "z"],
[1, "a", "x"]
[2, "c", "y"],
[2, "b", "x"],
[3, "c", "z"],
[3, "b", "z"],
[3, "b", "y"],
[3, "a", "z"]]
For a descending column reverse the left and right elements of the spaceship operator.
One way to do this is to do a series of 'stable sorts' in reverse order. Start with the inner sort and work out to the outer. The stability property means that the inner sort order remains intact.
Unfortunately, Ruby's sort is not stable. But see this question for a workaround.
# Sort on each entry in `ticket_columns`, starting with the first column, then second, etc.
# Complex sorts are supported. If the first element in each `ticket_columns` is a true/false
# boolean (specifying if an ascending sort should be used), then it is sorted that way.
# If omitted, it will sort all ascending.
def _subsort_columns(*ticket_columns)
# Is the first element of every `ticket_column` a boolean?
complex_sort = ticket_columns.all? { |e| [TrueClass, FalseClass].include? e[0].class }
if complex_sort
data = ticket_columns.transpose
sort_directions = data.first
column_data = data[1..-1].flatten 1
sorted = column_data.transpose.sort do |cmp_first, cmp_last|
cmp_which = sort_directions.map { |b| b ? cmp_first : cmp_last }
cmp_these = sort_directions.map { |b| b ? cmp_last : cmp_first }
cmp_left, cmp_right = [], []
cmp_which.each_with_index { |e, i| cmp_left << e[i] }
cmp_these.each_with_index { |e, i| cmp_right << e[i] }
cmp_left <=> cmp_right
end
sorted
else
ticket_columns.transpose.sort
end
end

Get k-1 length subset arrays of k length array [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
ruby array element grouping
Example. Given array a:
a = [1, 2, 3]
Its length is 3 so I want to print all 2-length arrays. These are:
[1, 2]
[1, 3]
[2, 3]
I don't know if there is some method in Ruby to get subset arrays. If there is not such a method what is most efficient way to do achieve this.
That's just a simple combination of 2 elements:
>> xs = [1, 2, 3]
>> xs.combination(xs.size - 1).to_a
=> [[1, 2], [1, 3], [2, 3]]
[EDIT] As #Joshua pointed out in a comment, the docs state that the order is not guaranteed (!). So here is a functional implementation that generates the combinations in the order you asked for. For completeness, I'll make it lazy as the original combination method:
require 'enumerable/lazy'
class Array
def combinations_of(n)
if n == 0
[[]].lazy
else
0.upto(self.size - 1).lazy.flat_map do |idx|
self.drop(idx + 1).combinations_of(n - 1).map do |xs|
[self[idx]] + xs
end
end
end
end
end

Resources