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 answer
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]]
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
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}>
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}
Iterate over the objects and make sure each following element is >= the current element (or previous is <=, obviously) the current element.