Home > Blockchain >  RUBY: How do I subtract each element from left to right and return the max difference?
RUBY: How do I subtract each element from left to right and return the max difference?

Time:05-09

I'm so sorry for the basic question. Studied coding for a month now and I still blank out at data structure questions.

array = [6,2,3,7]

6 cannot be subtracted with something

2 - 6 = -4

3 - 2 = 1

7 - 3 = 4

so 4 is the max difference

CodePudding user response:

The Ruby-like way is to use Enumerable#each_cons, which @Joel has done in his answer. I will present an alternative approach.


Let's create a method max_consecutive_diff that takes a single argument, an array of integers.

def max_consecutive_diff(arr)
  ...
end

We should start by dealing with the case where the array arr is empty or contains a single integer. It appears from your example that the method should only be called when arr contains at least two elements. You therefore way want to check that at the beginning and raised an exception if arr contains zero or one element.

def max_consecutive_diff(arr)
  raise(ArgumentError, "arr contains fewer than two elements") if arr.size < 2
  ...
end

In the following I will use the array

arr = [6,2,3,7]

for illustration.


We first need to compute an array of element differences. Suppose we begin by computing an array of the indices of all but the first element of array: [1,2,3]. Then we can use Array#map to map those indices to the associated differences:

indices = [1,2,3]
diffs = indices.map { |i| arr[i]-arr[i-1] }
  #=> [-4, 1, 4]

There are various ways to compute indices. Here is one, that uses the class method Array::new:

indices = Array.new(array.size-1) { |i| i 1 }
   #=> [1, 2, 3]

Another way is to compute a range of indices:

indices = 1..arr.size-1
  #=> 1..3

and then write

diffs = indices.map { |i| arr[i]-arr[i-1] }
  #=> [-4, 1, 4]

This does not use, Array#map, as map's receiver is a range, not an array. Instead, Ruby sees that this is Enumerable#map). Enumerable is a module and not a class. As

Range.included_modules 
  #=> [Enumerable, Kernel]

we see that the class Range has included the module Enumerable, so it has access to all of its instance methods. In time you will learn the details of how this works and why it is so useful.

Note that storage requirements are reduced by using a range instead of an array. I believe that most Rubyists would use a range here.


Regardless of how diffs is computed, the final step is to compute its largest value, using Array#max:

diffs.max
  #=> 4

Our method now becomes

def max_consecutive_diff(arr)
  raise(ArgumentError, "arr contains fewer than two elements") if arr.size < 2
  indices = 1..arr.size-1
  diffs = indices.map { |i| arr[i]-arr[i-1] }
  return diffs.max
end

We can simplify this by substituting out the variables indices and diffs, and removing the keyword return. We can do the latter because when return is not present Ruby will return the result of the last calculation made by the method.

def max_consecutive_diff(arr)
  raise(ArgumentError, "arr contains fewer than two elements") if arr.size < 2
  (1..arr.size-1).map { |i| arr[i]-arr[i-1] }.max
end

It's entirely up to you to decide whether to make these simplifications. That is, it's a question of programming style.


Let's try it.

max_consecutive_diff([])
  #=> ArgumentError: arr contains fewer than two elements
max_consecutive_diff([1])
  #=> ArgumentError: arr contains fewer than two elements
max_consecutive_diff([1,'cat'])
  #=> NoMethodError: undefined method `-' for "cat":String
max_consecutive_diff([1,2])
  #=> 1
max_consecutive_diff([2,1])
  #=> -1
max_consecutive_diff([1,2,4])
  #=> 2
max_consecutive_diff([1,2,5,4,6])
  #=> 3

CodePudding user response:

array.each_cons(2).map { |a,b| b-a }.max

https://ruby-doc.org/core-3.1.1/Enumerable.html#method-i-each_cons

  • Related