Sort an Array Mixed With Integers and Strings - Ruby - ruby

I have an array that must be sorted with low number to high number and then alphabetical order. Must use Array#sort_by
i_want_dogs = ["I", "want", 5, "dogs", "but", "only", "have", 3]
I want it to output:
=> [3,5,"I","but","dogs","have","only","want"]
I tried:
i_want_dogs.sort_by {|x,y| x <=> y }
I know that is obviously wrong, but I can't figure it out with the integers and the strings combined.

Use the sort method with a block that defines a comparator that does what you want. I wrote a simple one that compares values when the classes are the same and class names when they are different.
def comparator(x, y)
if x.class == y.class
return x <=> y
else
return x.class.to_s <=> y.class.to_s
end
end
Use it like this:
i_want_dogs.sort { |x, y| comparator(x, y) }

Use partition to separate numbers from strings, sort each separately and join the final result, e.g.
i_want_dogs.partition { |i| i.is_a?(Fixnum) }.map(&:sort).flatten

This will give you the result:
i_want_dogs.sort_by {|x| x.to_s }
UPDATE:
Thanks #vacawama who points out that it will sort numbers alphabetically. If you need to sort number by it's value, other answers will be something you need to try.

First you need to convert the elements in the array to a string. Try this
i_want_dogs.sort_by(&:to_s)
This will return
[3,5,"I", "but", "dogs", "have", "only" "want"]

Related

Is there a function to find the max values index in an array containing ints and strings?

In the example here, is there a way to print out the max int (7654) index (5) properly?
I havent been able to figure out a way to do this with an array containing ints and strings, only ones with strictly ints.
array = ["me", 2345, "you", 345, "him", 7654, "her", 25]
arraybutonlynumbers = [2345, 345, 7654, 25]
puts array.each_with_index.max[1] #comparison of Array with Array failed (ArgumentError)
puts arraybutonlynumbers.each_with_index.max[1] #no error
Using Select, Max, and Index
You can find the intermediate results you need in three conceptual steps using built-in Array methods:
Use Array#select to consider only Integer values.
Capture the largest Integer value with Array#max.
Search the original Array for the captured Integer, and return the index of that element with Array#index.
You would then use some or all of those return values to craft your expected output. To illustrate the general approach from the irb console:
# find largest integer in a mixed array
array.select { |e| e.is_a? Integer }.max
#=> 7654
# find index of last return value
array.index _
#=> 5
However, to get the output you want, you'll need to refactor this into something that keeps the intermediate results so you can return them in the format you expect. For example:
def max_integer_with_index array
max_int = array.select { |e| e.is_a? Integer }.max
max_int_idx = array.index max_int
[max_int, max_int_idx]
end
max_integer_with_index [
"me", 2345, "you", 345, "him", 7654, "her", 25
]
#=> [7654, 5]
You can also reduce finding the index to a single line of code if you don't need the intermediate values. For example:
array.index array.select { |e| e.is_a? Integer }.max
#=> 5
Caveat
Please note that if you want to do something else besides ignore the String objects in your array, you will probably need to implement Array#sort_by (inherited from Enumerable; see also Comparable) to draw your own custom comparisons between Integers and Strings.
array = ["me", 2345, "you", 345, "him", 7654, "her", 25]
element, index = array.each_with_index.max_by{| el, idx| el.to_i}
p element, index
# => 7654
# => 5
Note however that to_i converts strings which do not start with a digit to 0, which may produce unwanted results if there are no positive integers in the array.

How to determine whether an array is contained in another array

The question is, given [1,2,3,4,5] and [2,4,5], to determine whether (every element in) the second array is contained in the first one. The answer is true.
What's the most succinct and efficient way to do better than:
arr2.reject { |e| arr1.include?(e) } .empty?
Array subtraction should work, as in
(arr2 - arr1).empty?
Description of method:
Returns a new array that is a copy of the original array, removing any
items that also appear in [the second array]. The order is preserved from the
original array.
It compares elements using their hash and eql? methods for efficiency.
I don't consider myself an expert on efficiency, but #Ryan indicated in comments to his answer that it's reasonably efficient at scale.
The bad O(n²) one-liner would look like this:
arr2.all? { |x| arr1.include? x }
arr2.all? &arr1.method(:include?) # alternative
If your objects are hashable, you can make this O(n) by making a set out of the first array:
require 'set'
arr2.all? &Set.new(arr1).method(:include?)
If your objects are totally, like, ordered, you can make it O(n log n) with a sort and a binary search:
arr1.sort!
arr2.all? { |x| arr1.bsearch { |y| x <=> y } }
As mentioned by #Ryan you can use sets. In which case Set#subset? is available to you which is pretty readable (note the two different ways of defining a set from an array):
require 'set'
s1 = Set.new([1, 2, 3])
s2 = [1, 2].to_set
s3 = [1, 3].to_set
s4 = [1, 4].to_set
s1.subset? s1 #=> true
s2.subset? s1 #=> true
s3.subset? s1 #=> true
s4.subset? s1 #=> false
Also consider using Set#proper_subset if required.
s1.proper_subset? s1 #=> false
s2.proper_subset? s1 #=> true
NB A set contains no duplicate elements e.g. Set.new([1,2,3,3]) #=> #<Set: {1, 2, 3}>

Find the largest value for an array of hashes with common keys?

I have two arrays, each containing any number of hashes with identical keys but differing values:
ArrayA = [{value: "abcd", value_length: 4, type: 0},{value: "abcdefgh", value_length: 8, type: 1}]
ArrayB = [{value: "ab", value_length: 2, type: 0},{value: "abc", value_length: 3, type: 1}]
Despite having any number, the number of hashes will always be equal.
How could I find the largest :value_length for every hash whose value is of a certain type?
For instance, the largest :value_length for a hash with a :type of 0 would be 4. The largest :value_length for a hash with a :type of 1 would be 8.
I just can't get my head around this problem.
A simple way:
all = ArrayA + ArrayB # Add them together if you want to search both arrays.
all.select{|x| x[:type] == 0}
.max_by{|x| x[:value_length]}
And if you wanna reuse it just create a function:
def find_max_of_my_array(arr,type)
arr.select{|x| x[:type] == type}
.max_by{|x| x[:value_length]}
end
p find_max_of_my_array(ArrayA, 0) # => {:value=>"abcd", :value_length=>4, :type=>0}
I'm not totally sure I know what the output you want is, but try this. I assume the arrays are ordered so that ArrayA[x][:type] == ArrayB[x][:type] and that you are looking for the max between (ArrayA[x], ArrayB[x]) not the whole array. If that is not the case, then the other solutions that concat the two array first will work great.
filtered_by_type = ArrayA.zip(ArrayB).select{|x| x[0][:type] == type }
filtered_by_type.map {|a| a.max_by {|x| x[:value_length] } }
Here's how I approached it: You're looking for the maximum of something, so the Array#max method will probably be useful. You want the actual value itself, not the containing hash, so that gives us some flexibility. Getting comfortable with the functional programming style helps here. In my mind, I can see how select, map, and max fit together. Here's my solution which, as specified, returns the number itself, the maximum value:
def largest_value_length(type, hashes)
# Taking it slowly
right_type_hashes = hashes.select{|h| h[:type] == type}
value_lengths = right_type_hashes.map{|h| h[:value_length]}
maximum = value_lengths.max
# Or, in one line
#hashes.select{|h| h[:type] == type}.map{|h| h[:value_length]}.max
end
puts largest_value_length(1, ArrayA + ArrayB)
=> 8
You can also sort after filtering by type. That way you can get smallest, second largest etc.
all = ArrayA + ArrayB
all = all.select { |element| element[:type] == 1 }
.sort_by { |k| k[:value_length] }.reverse
puts all[0][:value_length]
#8
puts all[all.length-1][:value_length]
#3

problems with simple sorting of hashes in array being ignored

I have a simple array
my_array = [{id:1,priority_score:2},{id:3,priority_score:5},{id:4,priority_score:3.5}...]
I am trying to sort by the priority score with
sort_array = my_array.sort_by{|priority| priority[:priority_score]}
strangely, I am not getting a back any difference from my original array.
I create the priority score using
new_hash = {id:h.id,priority_score:(a.score+b.score)/2}
and have added all sorts of .to_f,.to_i in case the problem was that the priority score was not being recognized as a number, but that isn't the problem.
Any suggestions?
you can use sort_by like this.
my_array.sort_by {|(h, _)| h[:priority_score]}
# [{:id=>1, :priority_score=>2}, {:id=>4, :priority_score=>3.5}, {:id=>3, :priority_score=>5}]
The Array#sort method takes a block with two arguments, a and b, and wants you to compare them by hand. Given how you invoke it, I think you wanted sort_by, which takes a block with each item in the array, performs some transformation, and sorts by the result.
The sort gets two elements and the sorting is done by the comparison result of these elements.
This comparison should return -1, 0, 1.
0 for equal, -1 first < second and +1 first > second.
In most of the cases you can use ruby built in function <=>
For example:
my_array = [{id:1,priority_score:2},{id:3,priority_score:5},{id:4,priority_score:3.5}]
# Sort ascending
cr = my_array.sort {|a,b| a[:priority_score] <=> b[:priority_score]}
#[{:id=>1, :priority_score=>2}, {:id=>4, :priority_score=>3.5}, {:id=>3, :priority_score=>5}]
# Sort descending
de = my_array.sort {|a,b| b[:priority_score] <=> a[:priority_score]}
#[{:id=>3, :priority_score=>5}, {:id=>4, :priority_score=>3.5}, {:id=>1, :priority_score=>2}]

How do I detect duplicate values within an array in Ruby?

Say I have an array that looks like:
a = [cat, dog, cat, mouse, rat, dog, cat]
How do I cycle through that, and do something with duplicates - e.g. say delete them?
In other words, if I did a.each do |i|, how do I evaluate a[0], against a[1], a[2], a[3]...and then when I find the one I want, say a[2] in this case has the first duplicate, I then push it to a stack or remove it or something.
I know how to evaluate keys, versus values...but how do I evaluate values against each other within the same array?
Thanks.
You can create a hash to store number of times any element is repeated. Thus iterating over array just once.
h = Hash.new(0)
['a','b','b','c'].each{ |e| h[e] += 1 }
Should result
{"a"=>1, "b"=>2, "c"=>1}
This works efficiently and is rather simple:
require 'set'
visited = Set.new
array.each do |element|
if visited.include?(element)
# duplicated item
else
# first appearance
visited << element
end
end
Try this:
class Array
def find_dups
uniq.map {|v| (self - [v]).size < (self.size - 1) ? v : nil}.compact
end
end
a = ['cat', 'dog', 'cat', 'mouse', 'rat', 'dog', 'cat']
print a - a.find_dups # Removes duplicates
find_dups will return elements that have duplicates
Try this:
array.inject({}){|h, e| h[e] = h[e].to_i + 1; h}
Use
a.uniq! to remove duplicates .
also checkout the ruby-doc.org where you can find more info on ruby's class methods .
A simple solution is to run a double loop:
a.each_with_index do |a1, idx1|
a.each_with_index do |a2, idx2|
next if idx1 >= idx2 # Don't compare element to itself
# and don't repeat comparisons already made
# do something with a pair of elements (a1, a2)
end
end
If you just want to eliminate duplicates, there's a method: Array#uniq.
This will print all the duplicates in an array:
array.inject(Hash.new(0)) { |hash,val|
hash[val] += 1;
hash
}.each_pair { |val,count|
puts "#{val} -> #{count}" if count > 1
}
The best way to do it is to compare it with a unique version of itself. If its the same then it has no duplicates, if not then duplicates exist.
unique_array = original_array.uniq
get a unique version of your array
if original_array == unique_array then return true else return false
compare it to your original array.
Simple!
If you just want to get rid of duplicates, the easiest thing to do is take the array and do array&array. Use the & operator.
If you want to know what those repeats are, just compare array to array&array.
If array is sortable, then something like below will return only the duplicates.
array.sort.each_cons(2).select {|p| p[0] == p[1] }.map &:first
Sorts the array, then maps it to consecutive pairs of elements, selects pairs which are same, maps to elements.

Resources