Check to see if an array is already sorted? - ruby

I know how to put an array in order, but in this case I just want to see if it is in order. An array of strings would be the easiest, I imagine, and answers on that front are appreciated, but an answer that includes the ability to check for order based on some arbitrary parameter is optimal.
Here's an example dataset. The name of:
[["a", 3],["b",53],["c",2]]
Where the elements are themselves arrays containing several elements, the first of which is a string. I want to see if the elements are in alphabetical order based on this string.

It looks like a generic abstraction, let's open Enumerable:
module Enumerable
def sorted?
each_cons(2).all? { |a, b| (a <=> b) <= 0 }
end
end
[["a", 3], ["b", 53],["c", 2]].sorted? #=> true
Notice that we have to write (a <=> b) <= 0 instead of a <= b because there are classes that support <=> but not the comparator operators (i.e. Array), since they do not include the module Comparable.
You also said you'd like to have the ability "to check for order based on some arbitrary parameter":
module Enumerable
def sorted_by?
each_cons(2).all? { |a, b| ((yield a) <=> (yield b)) <= 0 }
end
end
[["a", 3], ["b", 1], ["c", 2]].sorted_by? { |k, v| v } #=> false
Using lazy enumerables (Ruby >= 2.1), we can reuse Enumerable#sorted?:
module Enumerable
def sorted_by?(&block)
lazy.map(&block).sorted?
end
end

You can compare them two by two:
[["a", 3],["b",53],["c",2]].each_cons(2).all?{|p, n| (p <=> n) != 1} # => true

reduce can compare each element to the one before, and stop when it finds one out of order:
array.reduce{|prev,l| break unless l[0] >= prev[0]; l}

If it turns out the array isn't sorted, will your next action always be to sort it? For that use case (though of course depending on the number of times the array will already be sorted), you may not want to check whether it is sorted, but instead simply choose to always sort the array. Sorting an already sorted array is pretty efficient with many algorithms and merely checking whether an array is already sorted is not much less work, making checking + sorting more work than simply always sorting.

def ascending? (array)
yes = true
array.reduce { |l, r| break unless yes &= (l[0] <= r[0]); l }
yes
end
def descending? (array)
yes = true
array.reduce { |l, r| break unless yes &= (l[0] >= r[0]); l }
yes
end

Iterate over the objects and make sure each following element is >= the current element (or previous is <=, obviously) the current element.

For this to work efficiently you will want to sort during insertion.
If you are dealing with unique items, a SortedSet is also an option.
For clarification, if we patch array to allow for a sorted insertion, then we can keep the array in a sorted state:
class Array
def add_sorted(o)
size = self.size
if size == 0
self << o
elsif self.last < o
self << o
elsif self.first > o
self.insert(0, o)
else
# This portion can be improved by using a binary search instead of linear
self.each_with_index {|n, i| if n > o; self.insert(i, o); break; end}
end
end
end
a = []
12.times{a.add_sorted(Random.rand(10))}
p a # => [1, 1, 2, 2, 3, 4, 5, 5, 5, 5, 7]
or to use the built in sort:
class Array
def add_sorted2(o)
self << o
self.sort
end
end
or, if you are dealing with unique items:
require "set"
b = SortedSet.new
12.times{b << Random.rand(10)}
p b # => #<SortedSet: {1, 3, 4, 5, 6, 7, 8, 9}>

These are all way too hard. You don't have to sort, but you can use sort to check. Scrambled array below for demonstration purposes.
arr = [["b",3],["a",53],["c",2]]
arr.sort == arr # => false
p arr.sort # => [["a",53],["b",3],["c",2]]

Related

Ruby combined comparison operator (<=>) and min / max / minmax functions

I understand #max, #min, #minmax. I understand <=>. But how does it work in a block within one of those functions?
That is, what is happening in the third line below? What is <=> doing in #min?
a = %w(albatross dog horse)
a.min #=> "albatross"
a.min { |a, b| a.length <=> b.length } #=> "dog"
example from http://ruby-doc.org/core-2.2.3/Enumerable.html#method-i-min
What behavior would it have for an array of numbers?
As you've probably already seen, the documentation for the min method says:
min(n) → array
min(n) {| a,b | block } → array
Returns the object in enum with the minimum value. The first form
assumes all objects implement Comparable; the second uses the block to
return a <=> b.
This means that, in the first form, min is calling the <=> method on objects in the array and using the result to determine which element is the smallest.
In the second form, min instead calls the block with both of the elements it wants to compare, and uses the block's return value to determine which element is the smallest. Basically, it's using the block as if it were an implementation of the <=> operator. So x.min {|a,b| a <=> b } would be equivalent to x.min.
In the example given (a.min { |a, b| a.length <=> b.length } #=> "dog"), this means that instead of comparing each element to determine sort order, it's comparing the lengths of each element to make that determination. Since "dog" is the shortest string in the list, that's the value that gets returned by min. max, minmax, and sort behave similarly.
Note that the example there is a bit contrived, since you could just use min_by in that situation to achieve the same result with simpler code: a.min_by { |x| x.length }. If you want more fine-grained control though when determining sort order, using min with a block might be appropriate.
What behavior would it have for an array of numbers?
min behaves the same way regardless of what the array contains. In this case though using the block { |a, b| a.length <=> b.length } wouldn't work since numbers don't have a length method on them. Here's a better example for numbers, which sorts by smallest to biggest, but always counts odd numbers as being bigger than even numbers:
[2, 10, 9, 7, 6, 1, 5, 3, 8, 4].sort do |a, b|
if a.odd? && b.even?
1
elsif a.even? && b.odd?
-1
else
a <=> b
end
end
Result:
[2, 4, 6, 8, 10, 1, 3, 5, 7, 9]
Notice how even numbers are sorted before odd numbers in the final array? That's the result of the block we passed to sort. The behavior is similar for min, max, and minmax.
min passes two elements a and b from the array to the block and the block is expected to return -1, 0, or +1 depending on whether a is less than, equal to, or greater than b. The "spaceship" operator <=> returns these -1, 0, or +1 values.
The algorithm is easy. Given a comparison function:
cmp = -> (a, b) { a.length <=> b.length }
We start by comparing the 1st with the 2nd element:
cmp.call 'albatros', 'dog'
#=> 1
1 means 'albatros' is greater than 'dog'. We continue with the lesser value, i.e. 'dog' and compare it with the 3rd element:
cmp.call 'dog', 'horse'
#=> -1
-1 means 'dog' is less than 'horse'. There are no more elements, so 'dog' is the result.
You could also implement this algorithm in Ruby:
def my_min(ary)
ary.inject { |min, x| yield(x, min) == -1 ? x : min }
end
ary = %w(albatross dog horse)
my_min(ary) { |a, b| a.length <=> b.length }
#=> "dog"

Ruby: how can Enumerator be used in the following example?

I can't seem to go beyond using Enumerators in a trivial way.
Maybe it's actually more limited than I realize.
Here is an example where I thought Enumerators would be handy, but couldn't implement them:
Let array = [0, 1, 2, "+", 4].
I'm interested in creating the following Enumerator-behavior:
iterate through the array
yield all objects that are not "+"
if it is a "+", delete that element and the previous two elements,
yield their sum, and reset the enumeration sequence to the
beginning.
Maybe the problem is that I can't "delete" something that has already been yielded? In other words, I can't un-yield? I'm interested in this behavior because of its ability to rewind the sequence at any given position after mutating the array (which messes up the order of iteration).
Enumerator does not have a previous method, it does not allow you to skip values while iterating and it does not have a method to delete elements.
So I think that you can't do what you want with the exact workflow you posted although I am sure it is possible to create the behaviour you seek when it is possible to deviate from your workflow.
Summary - this option seems to meet your criteria as expressed:
[0, 1, 2, "+", 4].inject([]) { |n,e| n << (e == "+" ? n.pop(2).reduce(:+) : e) }
# => [0, 3, 4]
Here was my thinking on it...
Option 1:
Use each_with_index and that way you can identify the previous elements easily.
array.each_with_index do |element, index|
your_logic_here
end
Option 2:
Or to avoid mutating the array you could just simply build a new one like this:
new_array = []
array.each do |element|
if element == "+"
ele_sum = new_array.pop(2)
new_array << (ele_sum.reduce :+)
else
new_array << element
end
end
And if you don't find it confusing you can combine the two lines in the if statement to:
new_array << new_array.pop(2).reduce(:+)
Or if you're okay with the ternary operator shorten the whole thing to:
def some_function(array)
new_array = []
array.each { |element| new_array << (element == "+" ? new_array.pop(2).reduce(:+) : element) }
end
> array = [0, 1, 2, "+", 4]
> some_function(array)
# => [0, 3, 4]
Works and tested in irb
...or use inject of course then you can do it all inline:
array.inject([]) { |new_array, element| new_array << (element == "+" ? new_array.pop(2).reduce(:+) : element) }
[0, 1, 2, "+", 4].inject([]) { |n,e| n << (e == "+" ? n.pop(2).reduce(:+) : e) }
# => [0, 3, 4]
But I prefer the expanded version for readability

How does <=> work for different sorting strategies?

I'm going through some tutorials on CodeAcademy, and came across this scenario:
books = ["Charlie and the Chocolate Factory", "War and Peace", "Utopia", "A Brief History of Time", "A Wrinkle in Time"]
# To sort our books in ascending order, in-place
books.sort! { |firstBook, secondBook| firstBook <=> secondBook }
# Sort your books in descending order, in-place below
# this lin initially left blank
books.sort! {|firstBook, secondBook| secondBook <=> firstBook}
Instead of using if/else blocks, I gave this a shot, and it worked, but I don't know why. I assume it doesn't matter which order you place the items in the check (i.e., a <=> b vs. b <=> a). Could someone explain what's happening here?
If you reverse the elements in <=> you reverse its value. If the elements are equal this operator returns 0, but if the first one is smaller it returns a negative value, if the first is greater it returns a positive value. Thus if temp = a <=> b then b <=> a is -temp. So you reverse the order of sorting if you write the arguments in reverse order.
Here's some simple visual ways of seeing what <=> does, and how reversing the order of the comparison variables affects the order of the output.
Starting with a basic Array:
foo = %w[a z b x]
We can do an ascending sort:
foo.sort { |i, j| i <=> j } # => ["a", "b", "x", "z"]
Or a descending sort by reversing the two variables being compared:
foo.sort { |i, j| j <=> i } # => ["z", "x", "b", "a"]
The <=> operator returns -1, 0 or 1, depending on whether the comparison is <, == or > respectively.
We can test that by negating the result of the comparison, which will reverse the order if the theory holds true.
foo.sort { |i, j| -(i <=> j) } # => ["z", "x", "b", "a"]
foo.sort { |i, j| -(j <=> i) } # => ["a", "b", "x", "z"]
By negating the result of the comparisons the order does reverse. But, for clarity in code, just reverse the order of the variables.
That all said, using sort, or its destructive sibling sort!, isn't always the fastest way to sort complex objects. Simple objects, like strings and characters, and numerics, sort extremely quickly because their classes implement the necessary methods to perform <=> tests quickly.
Some of the answers and comments mention sort_by, so let's go there.
Complex objects don't usually sort correctly, so we end up using getters/accessors to retrieve some value we want to compare against, and that action has a cost in CPU time. sort repeatedly compares the values so that retrieval would occur repeatedly, and add up as wasted time when sorting wasn't happening.
To fix that, a smart guy named Randall Schwartz, who's a major player in the Perl world, started using an algorithm that precomputes once the value to be used to sort; That algorithm is commonly called a Schwartzian Transform as a result. That value, and the actual object, are bundled together in a small sub-array, and then sorted. Because the sort occurs against the pre-computed value, it, and its associated object, are moved around in the ordering, until the sort completes. At that point, the actual objects are retrieved and returned as the result of the method. Ruby implements that type of sort using sort_by.
sort_by doesn't use <=> externally, so you can sort by simply telling it how to get at the value you want to compare against:
class Foo
attr_reader :i, :c
def initialize(i, c)
#i = i
#c = c
end
end
Here's the array of objects. Note that they are in the order they were created, but not sorted:
foo = [[1, 'z'], [26, 'a'], [2, 'x'], [25, 'b'] ].map { |i, c| Foo.new(i, c) }
# => [#<Foo:0x007f97d1061d80 #c="z", #i=1>,
# #<Foo:0x007f97d1061d58 #c="a", #i=26>,
# #<Foo:0x007f97d1061d30 #c="x", #i=2>,
# #<Foo:0x007f97d1061ce0 #c="b", #i=25>]
Sorting them by the integer value:
foo.sort_by{ |f| f.i }
# => [#<Foo:0x007f97d1061d80 #c="z", #i=1>,
# #<Foo:0x007f97d1061d30 #c="x", #i=2>,
# #<Foo:0x007f97d1061ce0 #c="b", #i=25>,
# #<Foo:0x007f97d1061d58 #c="a", #i=26>]
Sorting them by the character value:
foo.sort_by{ |f| f.c }
# => [#<Foo:0x007f97d1061d58 #c="a", #i=26>,
# #<Foo:0x007f97d1061ce0 #c="b", #i=25>,
# #<Foo:0x007f97d1061d30 #c="x", #i=2>,
# #<Foo:0x007f97d1061d80 #c="z", #i=1>]
sort_by doesn't respond as well to using a negated value as sort and <=>, so, based on some benchmarks done a while back on Stack Overflow, we know that using reverse on the resulting value is the fastest way to switch the order from ascending to descending:
foo.sort_by{ |f| f.i }.reverse
# => [#<Foo:0x007f97d1061d58 #c="a", #i=26>,
# #<Foo:0x007f97d1061ce0 #c="b", #i=25>,
# #<Foo:0x007f97d1061d30 #c="x", #i=2>,
# #<Foo:0x007f97d1061d80 #c="z", #i=1>]
foo.sort_by{ |f| f.c }.reverse
# => [#<Foo:0x007f97d1061d80 #c="z", #i=1>,
# #<Foo:0x007f97d1061d30 #c="x", #i=2>,
# #<Foo:0x007f97d1061ce0 #c="b", #i=25>,
# #<Foo:0x007f97d1061d58 #c="a", #i=26>]
They're somewhat interchangable, but you have to remember that sort_by does have overhead, which is apparent when you compare its times against sort times when running against simple objects. Use the right method at the right time and you can see dramatic speed-ups.
Its called a spaceship operator
If you have something like this
my_array = ["b","c","a"]
my_array.sort! does the compare the elements of the array since it knows that the letters of english alphabet have natural ordering, likewise if you have array of integers
my_array2 = [3,1,2]
my_array2.sort! will compare the elements and gives the result as [1,2,3]
but if you want to change how the comparison is made in an array of strings or complex objects you specify it using the <=> operator..
my_array3 = ["hello", "world how are" , "you"]
my_array3.sort! { |first_element, second_element| first_element <=> second_element }
so it will tell the sort method to compare like this:
Is first_element < second_element?
Is first_element = second_element?
Is first_element > second_element?
but if you take this stmt,
my_array3.sort! { |first_element, second_element| first_element <=> second_element }
the comparison is made as follows:
Is second_element < first_element?
Is second_element = first_element?
Is second_element > first_element?
So it does make a difference if you change the elements to be considered.

Each with index with object in Ruby

I am trying to iterate over an array and conditionally increment a counter. I am using index to compare to other array's elements:
elements.each_with_index.with_object(0) do |(element, index), diff|
diff += 1 unless other[index] == element
end
I can't get diff to change value even when changing it unconditionally.
This can be solved with inject:
elements.each_with_index.inject(0) do |diff, (element, index)|
diff += 1 unless other[index] == element
diff
end
But I am wondering if .each_with_index.with_object(0) is a valid construction and how to use it?
From ruby docs for each_with_object
Note that you can’t use immutable objects like numbers, true or false
as the memo. You would think the following returns 120, but since the
memo is never changed, it does not.
(1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
So each_with_object does not work on immutable objects like integer.
You want to count the number of element wise differences, right?
elements = [1, 2, 3, 4, 5]
other = [1, 2, 0, 4, 5]
# ^
I'd use Array#zip to combine both arrays element wise and Array#count to count the unequal pairs:
elements.zip(other).count { |a, b| a != b } #=> 1

Looping through an array with step

I want to look at every n-th elements in an array. In C++, I'd do this:
for(int x = 0; x<cx; x+=n){
value_i_care_about = array[x];
//do something with the value I care about.
}
I want to do the same in Ruby, but can't find a way to "step". A while loop could do the job, but I find it distasteful using it for a known size, and expect there to be a better (more Ruby) way of doing this.
Ranges have a step method which you can use to skip through the indexes:
(0..array.length - 1).step(2).each do |index|
value_you_care_about = array[index]
end
Or if you are comfortable using ... with ranges the following is a bit more concise:
(0...array.length).step(2).each do |index|
value_you_care_about = array[index]
end
array.each_slice(n) do |e, *_|
value_i_care_about = e
end
Just use step() method from Range class which returns an enumerator
(1..10).step(2) {|x| puts x}
We can iterate while skipping over a range of numbers on every iteration e.g.:
1.step(10, 2) { |i| print "#{i} "}
http://www.skorks.com/2009/09/a-wealth-of-ruby-loops-and-iterators/
So something like:
array.step(n) do |element|
# process element
end
class Array
def step(interval, &block)
((interval -1)...self.length).step(interval) do |value|
block.call(self[value])
end
end
end
You could add the method to the class Array
What about:
> [1, 2, 3, 4, 5, 6, 7].select.each_with_index { |_,i| i % 2 == 0 }
=> [1, 3, 5, 7]
Chaining of iterators is very useful.
This is a great example for the use of the modulo operator %
When you grasp this concept, you can apply it in a great number of different programming languages, without having to know them in and out.
step = 2
["1st","2nd","3rd","4th","5th","6th"].each_with_index do |element, index|
puts element if index % step == 1
end
#=> "2nd"
#=> "4th"
#=> "6th"

Resources