Simple array sort and capitalize - ruby

New to Ruby and trying some stuff.
The code below is to convert the array to a string while sorting it and display the sorted results. Where I'm struggling is the use of the capitalize method to caps the all the sorted words.
the_data = ["dog", "cat", "fish", "zebra", "swan", "rabbit", "horse", "albatros", "frog", "mouse", "duck"]
puts "\nThe array:\n"
puts the_data
puts "\n"
puts "\nThe sorted array, capitalized:\n"
to_display = the_data.sort.join(("\n").capitalize)
puts to_display

You can use Array#map to capitalize each word of the Array
to_display = the_data.sort.map(&:capitalize).join("\n")
# => "Albatros\nCat\nDog\nDuck\nFish\nFrog\nHorse\nMouse\nRabbit\nSwan\nZebra"
If you want to capitalize all the letters, you can use upcase
to_display = the_data.sort.map(&:upcase).join("\n")
# => "ALBATROS\nCAT\nDOG\nDUCK\nFISH\nFROG\nHORSE\nMOUSE\nRABBIT\nSWAN\nZEBRA"

Related

Creating a map from an array of arrays in Ruby

I'm trying to write a function in Ruby which can take in a in an array of arrays, and convert it into a hash. This will be used to simulate sentences and create arbitrary word sequences. The resulting map will be helpful in generating all combination of sentences available with the current sentence rules.
How do I go about achieving this? I'm a bit lost as to where to start.
Try this:
arr = [ ["<start>", "The <object> <verb> tonight."],
["<object>", "waves", "big yellow flowers", "slugs"],
["<verb>", "sigh <adverb>", "portend like <object>", "die <adverb>"],
["<adverb>", "warily", "grumpily"] ]
arr.map { |ar| [ar.shift, ar.map { |str| str.split }] }.to_h
#=>
#{ "<start>" => [["The", "<object>", "<verb>", "tonight."]],
# "<object>" => [["waves"], ["big", "yellow", "flowers"], ["slugs"]],
# "<verb>" => [["sigh", "<adverb>"], ["portend", "like", "<object>"], ["die", "<adverb>"]],
# "<adverb>" => [["warily"], ["grumpily"]] }
ar.shift takes the first element of each subarray. The block used with ar.map splits (on whitespace) the remaining elements into arrays. Finally to_h converts the resulting array into a hash.

Search an array for a string and its substrings in ruby

I want to search an array for a certain string and(!) its substrings. For example my array is:
array = ["hello", "hell", "goodbye", "he"]
So when I search for "hello" and its substrings (but only from the beginning: "he", "hell", "hello"), it should return
=> ["hello", "hell", "he"]
What I've tried so far: Using a regular expression with the #grep and/or the #include? method like this:
array.grep("hello"[/\w+/])
or
array.select {|i| i.include?("hello"[/\w+/])}
but in both cases it only returns
=> ["hello"]
By the way, if I try array.select{|i| i.include?("he")} it works but like I said I want it the other way around: searching for "hello" and give me all results including the substrings from the beginning.
require "abbrev"
arr = ["hello", "hell", "goodbye", "he"]
p arr & ["hello"].abbrev.keys # => ["hello", "hell", "he"]
array = ["hello", "hell", "goodbye", "he", "he"]
# define search word:
search = "hello"
# find all substrings of this word:
substrings = (0..search.size - 1).each_with_object([]) { |i, subs| subs << search[0..i] }
#=> ["h", "he", "hel", "hell", "hello"]
# find intersection between array and substrings(will exclude duplicates):
p array & substrings
#=> ["hello", "hell", "he"]
# or select array elements that match any substring(will keep duplicates):
p array.select { |elem| substrings.include?(elem) }
#=> ["hello", "hell", "he", "he"]
I'd use String#[] :
array = ["hello", "hell", "goodbye", "he", "he"]
search = "hello"
array.select { |s| search[/\A#{s}/] }
# => ["hello", "hell", "he", "he"]
Turn all the characters other than h in hello to optional.
> array = ["hello", "hell", "goodbye", "he"]
> array.select{|i| i[/^he?l?l?o?/]}
=> ["hello", "hell", "he"]
You could still use a regular expression like this
#define Array
arr = ["hello", "hell", "goodbye", "he"]
#define search term as an Array of it's characters
search = "hello".split(//)
#=> ['h','e','l','l','o']
#deem the first as manditory search.shift
#the rest are optional ['e?','l?','l?','o?'].join
search = search.shift << search.map{|a| "#{a}?"}.join
#=> "he?l?l?o?"
#start at the beginning of the string \A
arr.grep(/\A#{search}/)
#=> ["hello", "hell", "he"]
Just as the question reads:
array.select { |w| "hello" =~ /^#{w}/ }
#=> ["hello", "hell", "he"]
Use array#keep_if
array = ["hello", "hell", he"]
substrings = array.keep_if{|a| a.start_with?('h')}
=> ["hello", "hell", "he"]

Ruby - Sort array into sub categories

Lets say that I have an array of words, such as this:
words = ["apple", "zebra", "boat", "dog", "ape", "bingo"]
and I want to sort them alphabetically , but group them like so:
sorted = [["ape", "apple"], ["bingo", "boat"], ["dog"], ["zebra"]]
How would I be able to do this in Ruby? Help appreciated.
I'm assuming that you are trying to group by the first letter of each word. In which case you can use sort to sort the array and group_by to group by the first character of each word (as returned by chr).
words = ["apple", "zebra", "boat", "dog", "ape", "bingo"]
sorted = words.sort.group_by(&:chr).values
You could do something like this:
words.sort.chunk { |s| s[0] }.map(&:last)
This first sorts the array alphabetically (.sort), then it "chunks" together elements with the same first character (.chunk { |s| s[0] }), then it grabs the last element from each sub-array .map(&:last).
Something like this should do the trick
words = ["apple", "zebra", "boat", "dog", "ape", "bingo"]
sorted = words.sort.group_by { |s| s[0] }.map { |k,v| v }
You're going to need an array that also represents your categories. In this case, the letters of the alphabet. Below does what you're looking for using nested loops.
words = ["apple", "zebra", "boat", "dog", "ape", "bingo"]
results = []
('a'..'z').to_a.each_with_index do |letter, index|
letter_result = []
words.each do |word|
if(word[0]==letter)
letter_result.push(word)
end
end
unless letter_result.empty?
results.push(letter_result)
end
end

Ruby: Sorting an array of strings, in alphabetical order, that includes some arrays of strings

Say I have:
a = ["apple", "pear", ["grapes", "berries"], "peach"]
and I want to sort by:
a.sort_by do |f|
f.class == Array ? f.to_s : f
end
I get:
[["grapes", "berries"], "apple", "peach", "pear"]
Where I actually want the items in alphabetical order, with array items being sorted on their first element:
["apple", ["grapes", "berries"], "peach", "pear"]
or, preferably, I want:
["apple", "grapes, berries", "peach", "pear"]
If the example isn't clear enough, I'm looking to sort the items in alphabetical order.
Any suggestions on how to get there?
I've tried a few things so far yet can't seem to get it there. Thanks.
I think this is what you want:
a.sort_by { |f| f.class == Array ? f.first : f }
I would do
a = ["apple", "pear", ["grapes", "berries"], "peach"]
a.map { |e| Array(e).join(", ") }.sort
# => ["apple", "grapes, berries", "peach", "pear"]
Array#sort_by clearly is the right method, but here's a reminder of how Array#sort would be used here:
a.sort do |s1,s2|
t1 = (s1.is_a? Array) ? s1.first : s1
t2 = (s2.is_a? Array) ? s2.first : s2
t1 <=> t2
end.map {|e| (e.is_a? Array) ? e.join(', ') : e }
#=> ["apple", "grapes, berries", "peach", "pear"]
#theTinMan pointed out that sort is quite a bit slower than sort_by here, and gave a reference that explains why. I've been meaning to see how the Benchmark module is used, so took the opportunity to compare the two methods for the problem at hand. I used #Rafa's solution for sort_by and mine for sort.
For testing, I constructed an array of 100 random samples (each with 10,000 random elements to be sorted) in advance, so the benchmarks would not include the time needed to construct the samples (which was not insignificant). 8,000 of the 10,000 elements were random strings of 8 lowercase letters. The other 2,000 elements were 2-tuples of the form [str1, str2], where str1 and str2 were each random strings of 8 lowercase letters. I benchmarked with other parameters, but the bottom-line results did not vary significantly.
require 'benchmark'
# n: total number of items to sort
# m: number of two-tuples [str1, str2] among n items to sort
# n-m: number of strings among n items to sort
# k: length of each string in samples
# s: number of sorts to perform when benchmarking
def make_samples(n, m, k, s)
s.times.with_object([]) { |_, a| a << test_array(n,m,k) }
end
def test_array(n,m,k)
a = ('a'..'z').to_a
r = []
(n-m).times { r << a.sample(k).join }
m.times { r << [a.sample(k).join, a.sample(k).join] }
r.shuffle!
end
# Here's what the samples look like:
make_samples(6,2,4,4)
#=> [["bloj", "izlh", "tebz", ["lfzx", "rxko"], ["ljnv", "tpze"], "ryel"],
# ["jyoh", "ixmt", "opnv", "qdtk", ["jsve", "itjw"], ["pnog", "fkdr"]],
# ["sxme", ["emqo", "cawq"], "kbsl", "xgwk", "kanj", ["cylb", "kgpx"]],
# [["rdah", "ohgq"], "bnup", ["ytlr", "czmo"], "yxqa", "yrmh", "mzin"]]
n = 10000 # total number of items to sort
m = 2000 # number of two-tuples [str1, str2] (n-m strings)
k = 8 # length of each string
s = 100 # number of sorts to perform
samples = make_samples(n,m,k,s)
Benchmark.bm('sort_by'.size) do |bm|
bm.report 'sort_by' do
samples.each do |s|
s.sort_by { |f| f.class == Array ? f.first : f }
end
end
bm.report 'sort' do
samples.each do |s|
s.sort do |s1,s2|
t1 = (s1.is_a? Array) ? s1.first : s1
t2 = (s2.is_a? Array) ? s2.first : s2
t1 <=> t2
end
end
end
end
user system total real
sort_by 1.360000 0.000000 1.360000 ( 1.364781)
sort 4.050000 0.010000 4.060000 ( 4.057673)
Though it was never in doubt, #theTinMan was right! I did a few other runs with different parameters, but sort_by consistently thumped sort by similar performance ratios.
Note the "system" time is zero for sort_by. In other runs it was sometimes zero for sort. The values were always zero or 0.010000, leading me to wonder what's going on there. (I ran these on a Mac.)
For readers unfamiliar with Benchmark, Benchmark#bm takes an argument that equals the amount of left-padding desired for the header row (user system...). bm.report takes a row label as an argument.
You are really close. Just switch .to_s to .first.
irb(main):005:0> b = ["grapes", "berries"]
=> ["grapes", "berries"]
irb(main):006:0> b.to_s
=> "[\"grapes\", \"berries\"]"
irb(main):007:0> b.first
=> "grapes"
Here is one that works:
a.sort_by do |f|
f.class == Array ? f.first : f
end
Yields:
["apple", ["grapes", "berries"], "peach", "pear"]
a.map { |b| b.is_a?(Array) ? b.join(', ') : b }.sort
# => ["apple", "grapes, berries", "peach", "pear"]
Replace to_s with join.
a.sort_by do |f|
f.class == Array ? f.join : f
end
# => ["apple", ["grapes", "berries"], "peach", "pear"]
Or more concisely:
a.sort_by {|x| [*x].join }
# => ["apple", ["grapes", "berries"], "peach", "pear"]
The problem with to_s is that it converts your Array to a string that starts with "[":
"[\"grapes\", \"berries\"]"
which comes alphabetically before the rest of your strings.
join actually creates the string that you had expected to sort by:
"grapesberries"
which is alphabetized correctly, according to your logic.
If you don't want the arrays to remain arrays, then it's a slightly different operation, but you will still use join.
a.map {|x| [*x].join(", ") }.sort
# => ["apple", "grapes, berries", "peach", "pear"]
Sort a Flattened Array
If you just want all elements of your nested array flattened and then sorted in alphabetical order, all you need to do is flatten and sort. For example:
["apple", "pear", ["grapes", "berries"], "peach"].flatten.sort
#=> ["apple", "berries", "grapes", "peach", "pear"]

Split a string in Ruby

I have a hash returned to me in ruby
test_string = "{cat=6,bear=2,mouse=1,tiger=4}"
I need to get a list of these items in this form ordered by the number.
animals = [cat, tiger, bear, mouse]
My thoughts were to_s this in ruby and split on the '=' character. Then try to order them and put in a new list. Is there an easy way to do this in ruby? Sample code would be greatly appreciated.
s = "{cat=6,bear=2,mouse=1,tiger=4}"
a = s.scan(/(\w+)=(\d+)/)
p a.sort_by { |x| x[1].to_i }.reverse.map(&:first)
a = test_string.split('{')[1].split('}').first.split(',')
# => ["cat=6", "bear=2", "mouse=1", "tiger=4"]
a.map{|s| s.split('=')}.sort_by{|p| p[1].to_i}.reverse.map(&:first)
# => ["cat", "tiger", "bear", "mouse"]
Not the most elegant way to do it, but it works:
test_string.gsub(/[{}]/, "").split(",").map {|x| x.split("=")}.sort_by {|x| x[1].to_i}.reverse.map {|x| x[0].strip}
The below code should do it.
Explained the steps inline
test_string.gsub!(/{|}/, "") # Remove the curly braces
array = test_string.split(",") # Split on comma
array1= []
array.each {|word|
array1<<word.split("=") # Create an array of arrays
}
h1 = Hash[*array1.flatten] # Convert Array into Hash
puts h1.keys.sort {|a, b| h1[b] <=> h1[a]} # Print keys of the hash based on sorted values
test_string = "{cat=6,bear=2,mouse=1,tiger=4}"
Hash[*test_string.scan(/\w+/)].sort_by{|k,v| v.to_i }.map(&:first).reverse
#=> ["cat", "tiger", "bear", "mouse"]

Resources