Find the Unique last names from a .txt file in Ruby - ruby

I need to find the unique last names from a .txt file that looks like this:
Kent, Mackenna -- ut
Voluptatem ipsam et at.
Marven, Gardens -- non
Facere et necessitatibus animi.
McLaughlinn, Matt -- consequatur
Eveniet temporibus ducimus amet eaque.
Lang, August -- pariatur
Unde voluptas sit fugit.
Brad, Nick -- et
Maiores ab officia sed.
Adam, Levine -- error
Repellendus alias officia amet et perspiciatis.
Learner, York -- nesciunt
Incidunt et ut necessitatibus porro.
Ortiz, Andrew -- fuga
Tempore eos et hic.
Lang, Bryant -- et
Laborum perferendis inventore eveniet.
So far I have:
FNAME = 'example.txt'
# maps for last name in file
def last_name_from_file(file_name)
last_name = File.readlines(file_name).reject(&:empty?)
last_name.map do |line|
line.split.first
end
end
puts last_name_from_file('example.txt')
but this gives me the output which includes the Lorem text and the last name.
#Kent
#Voluptatem
#Marven
#Facere
#McLaughlinn
#Eveniet
#Lang
#Unde
#Brad
...

As I see lorem lines are even, so you can reject them.
def last_name_from_file(file_name)
File.
readlines(file_name).
reject.
with_index(1) { |_, id| id.even? }.
map { |line| line.split(',').first }.
uniq
end
Okay how would I go about getting the first name?
You can do method like this:
def names(file_name)
File.
readlines(file_name).
reject.
with_index(1) { |_, id| id.even? }.
map { |line| line.split(' --').first.split(', ') }.
map { |ln, fn| { lastname: ln, firstname: fn } }
end
And now you can call it:
names = names('example.txt')
names
# => [{:lastname=>"Kent", :firstname=>"Mackenna"}, {:lastname=>"Marven", :firstname=>"Gardens"}, {:lastname=>"McLaughlinn", :firstname=>"Matt"}, {:lastname=>"Lang", :firstname=>"August"}, {:lastname=>"Brad", :firstname=>"Nick"}, {:lastname=>"Adam", :firstname=>"Levine"}, {:lastname=>"Learner", :firstname=>"York"}, {:lastname=>"Ortiz", :firstname=>"Andrew"}, {:lastname=>"Lang", :firstname=>"Bryant"}]
names.map { |name| name[:lastname] }
# => ["Kent", "Marven", "McLaughlinn", "Lang", "Brad", "Adam", "Learner", "Ortiz", "Lang"]
names.map { |name| name[:firstname] }
# => ["Mackenna", "Gardens", "Matt", "August", "Nick", "Levine", "York", "Andrew", "Bryant"]

Let's create the file.
FName = 'temp.txt'
IO.write(FName,
<<~END
Kent, Mackenna -- ut
Voluptatem ipsam et at.
Marven, Gardens -- non
Facere et necessitatibus animi.
McLaughlinn, Matt -- consequatur
Eveniet temporibus ducimus amet eaque.
Lang, August -- pariatur
Unde voluptas sit fugit.
Brad, Nick -- et
Maiores ab officia sed.
Adam, Levine -- error
Repellendus alias officia amet et perspiciatis.
Learner, York -- nesciunt
Incidunt et ut necessitatibus porro.
O'Conner-Bolonzo, Andrew -- fuga
Tempore eos et hic.
Lang, Bryant -- et
Laborum perferendis inventore eveniet.
END
)
#=> 539
We can extract the unique last names by making a single pass through the file, reading line-by-line; there is no need to slurp the file into an array.
require 'set'
IO.foreach(FName).each_with_object(Set.new) do |line,set|
last_name = line[/\A[\p{Alpha}'-]+(?=,)/]
set << last_name unless last_name.nil?
end.to_a
#=> ["Kent", "Marven", "McLaughlinn", "Lang", "Brad", "Adam", "Learner",
# "O'Conner-Bolonzo"]
See IO::foreach, Enumerable#each_with_object, Set::new, Set#to_a and String#[].
Note that, when not given a block, IO::foreach returns an enumerator, and therefore can be chained, here to Enumerable#each_with_object.
The regular expression used by String#[] reads, "match one or more (+) characters at the beginning of the string (\A) that are letters, apostrophes or hyphens, immediately followed by a comma."

Something similar to my answer from yesterday:
def last_name_from_file(fname)
File.open(fname, "r").each_with_object([]).with_index do |(line, o), i|
o << line.split(',').first if i.even?
end.uniq
end
puts last_name_from_file('example.txt')
#Kent
#Marven
#McLaughlinn
#...

Related

Ignore Lorem Ipsum text in a file Ruby

I have a .txt file that has last name, first name on one line and on every other line I have Lorem Ipsum text. I need to detect the Lorem Ipsum in every other line and skip it.
example txt.file
Spade, Kate
Voluptatem ipsam et at.
Vuitton, Louis
Facere et necessitatibus animi.
Bucks, Star
Eveniet temporibus ducimus amet eaque.
Cage, Nicholas
Unde voluptas sit fugit.
Brown, James
Maiores ab officia sed.
expected output:
#Spade, Kate
#Vuitton, Louis
#Bucks, Star
#Cage, Nicholas
#Brown, James
Reading 2 lines and ignoring the second:
File.open("test.txt", "r") do |f|
f.each_slice(2) do |odd, _even|
puts odd
end
end
If you just want to skip every second line you can do something like this:
File.open("text.txt", "r") do |f|
f.each_line.with_index do |line, i|
next unless i.even?
puts line
end
end
#Spade, Kate
#Vuitton, Louis
#Bucks, Star
#Cage, Nicholas
#Brown, James
Now I'm not really good with regexp, but you could also do something like this to process only the lines that are two words, both starting with a capital letter separated by a comma and space (basically first name and last name):
File.open("text.txt", "r") do |f|
f.each_line do |line|
next unless line =~ /[A-Z][a-z]+, [A-Z][a-z]+/
puts line
end
end
#Spade, Kate
#Vuitton, Louis
#Bucks, Star
#Cage, Nicholas
#Brown, James
You could also load the full Lorem Ipsum text from a file like this:
lorem = File.open("lorem.txt", "r").map(&:chomp).join(" ")
And then check each line if it's contained in the Lorem Ipsum text:
File.open("text.txt", "r") do |f|
f.each_line do |line|
next if lorem.include?(line[0...-1]) #removing the last character because you seem to have a dot at the end even though in the lorem text there's no dot on these positions.
puts line
end
end
#Spade, Kate
#Vuitton, Louis
#Bucks, Star
#Cage, Nicholas
#Brown, James
Now depending on what you want to do with the data you can replace the puts line line with something else.
Your description is unclear. If you just want to skip every other line, you can do something like this:
File.foreach("test.txt").with_index(1) do |l, i|
next if i.even?
puts l
end
Let's first create a file.
FName = 'temp.txt'
IO.write(FName,
<<~END
Spade, Kate
Voluptatem ipsam et at.
Vuitton, Louis
Facere et necessitatibus animi.
Bucks, Star
Eveniet temporibus ducimus amet eaque.
Cage, Nicholas
Unde voluptas sit fugit.
Brown, James
Maiores ab officia sed.
END
)
#=> 211
Here's one way to return every other line.
IO.foreach(FName).each_slice(2).map(&:first)
#=> ["Spade, Kate\n", "Vuitton, Louis\n", "Bucks, Star\n",
# "Cage, Nicholas\n", "Brown, James\n"]
See IO::write, IO::foreach, Enumerable#each_slice and Array#map.
Note that foreach, each_slice and map all return enumerators when they are not given block. We therefore obtain the following:
enum0 = IO.foreach(FName)
#=> #<Enumerator: IO:foreach("temp.txt")>
enum1 = enum0.each_slice(2)
#=> #<Enumerator: #<Enumerator: IO:foreach("temp.txt")>:each_slice(2)>
enum2 = enum1.map
#=> #<Enumerator: #<Enumerator: #<Enumerator: IO:foreach("temp.txt")>
# :each_slice(2)>:map>
enum2.each(&:first)
#=> ["Spade, Kate\n", "Vuitton, Louis\n", "Bucks, Star\n",
# "Cage, Nicholas\n", "Brown, James\n"]
Examine the return values for the calculation of enum1 and enum2. It may be helpful to think of these as These could be thought of as compound enumerators.
Two other ways:
enum = [true, false].cycle
#=> #<Enumerator: [true, false]:cycle>
IO.foreach(FName).select { enum.next }
#=> <as above>
keep = false
IO.foreach(FName).select { keep = !keep }
#=> <as above>

Why is String#split("\n") and Array#join(' ') quicker than String#gsub(/\n/, ' ')?

I have to remove all newlines from a large number of strings. In benchmarking string.join("\n").split(' ') vs string.gsub(/\n/, ' '), I found that the split and join methods are much quicker, but have a hard time understanding why. I do not understand how splitting the string up into array elements each time it encounters a \n, then joining the array into a new string could possibly be quicker than just scan and replacing each \n with a ' '.
sentence = %q[
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae
dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius
modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea]
Check to verify the output is indeed the same for both methods:
puts sentence.split("\n").join(' ') == sentence.gsub(/\n/, ' ')
#=> true
Script used to benchmark:
def split_join_method(string)
start = Time.now;
1000000.times { string.split("\n").join(' ') }
puts "split_join: #{Time.now - start} s"
end
def gsub_method(string)
start = Time.now;
1000000.times { string.gsub(/\n/, ' ') }
puts "gsub: #{Time.now - start} s"
end
5.times do
split_join_method(sentence)
gsub_method(sentence)
end
Results:
#=> split_join: 6.753057 s
#=> gsub: 14.938358 s
#=> split_join: 6.16101 s
#=> gsub: 14.166971 s
#=> split_join: 5.946168 s
#=> gsub: 13.490355 s
#=> split_join: 5.781062 s
#=> gsub: 13.436135 s
#=> split_join: 5.903052 s
#=> gsub: 15.670774 s
I think gsub takes more time for two reasons:
The first is that using a regex engine has an initial cost, at least to parse the pattern.
The second and probably the most important here is that the regex engine works with a dumb walk character by character and tests the pattern for each positions in the string when the split (with a literal string here) uses a fast string search algorithm (probably the Boyer-Moore algorithm).
Note that even if the split/join way is faster, it uses probably more memory since this way needs to generate new strings.
Note2: some regex engines are able to use this fast string search algorithm before the walk to find positions, but I have no informations about this for the ruby regex engine.
Note3: It may be interesting to have a better idea of what happens to include tests with few repeatitions but with larger strings. [edit] After several tests with #spickermann code, it seems that it doesn't change anything (or nothing very significative) even with very few repetitions. So the initial cost may be not so important.
Your question is comparing apples and oranges, because you compare a regexp method with a string search operation.
My benchmark cannot reproduce your observation that split combined with join are in general faster that a simple gsub, the gsub version is always faster. The only thing I can confirm is, that regexp matches are slower than string searches what is not very surprising.
Btw. tr is the fastest solution for this kind of problem:
Rehearsal ---------------------------------------------------
string_split: 5.390000 0.100000 5.490000 ( 5.480459)
regexp_split: 14.220000 0.160000 14.380000 ( 14.413509)
string_gsub : 3.750000 0.090000 3.840000 ( 3.832316)
regexp_gsub : 12.890000 0.130000 13.020000 ( 13.045899)
string_tr : 2.480000 0.050000 2.530000 ( 2.525891)
----------------------------------------- total: 39.260000sec
user system total real
string_split: 5.450000 0.090000 5.540000 ( 5.543735)
regexp_split: 14.340000 0.190000 14.530000 ( 14.552214)
string_gsub : 4.160000 0.120000 4.280000 ( 4.543941)
regexp_gsub : 13.570000 0.200000 13.770000 ( 14.356955)
string_tr : 2.390000 0.040000 2.430000 ( 2.431676)
Code that I used for this benchmark:
require 'benchmark'
#string = %q[
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae
dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius
modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea]
def string_split
#string.split("\n").join(' ')
end
def regexp_split
#string.split(/\n/).join(' ')
end
def string_gsub
#string.gsub("\n", ' ')
end
def regexp_gsub
#string.gsub(/\n/, ' ')
end
def string_tr
#string.tr("\n", ' ')
end
n = 1_000_000
Benchmark.bmbm(15) do |x|
x.report("string_split:") { n.times do; string_split; end }
x.report("regexp_split:") { n.times do; regexp_split; end }
x.report("string_gsub :") { n.times do; string_gsub ; end }
x.report("regexp_gsub :") { n.times do; regexp_gsub ; end }
x.report("string_tr :") { n.times do; string_tr ; end }
end
It's because in your gsub code, you are using regular expressions, which are slow for the reasons pointed out in Casimir's answer. Here is proof: if you change
string.gsub(/\n/, ' ')
to
string.gsub("\n", ' ')
then the gsub code is actually faster than the split/join code.

Find common words in sentences with Ruby

I have a task to find words that are in each sentence.
Given a string and we want to divide the string into sentences and then determine which words, if any, are in all the sentences.
Here is my solution:
# encoding: utf-8
text = ''
File.foreach("lab2.in") do |line|
text += line
end
hash = Hash.new
text = text.gsub(/[\n,]/,'').split(/[!.?]/)
number = 0
text.each do |sen|
number += 1
words = sen.split(/ /)
words.each do |word|
if hash[word]
hash[word] += "#{number}"
else
hash[word] = "#{number}"
end
end
end
flag = false
needle = ''
count = text.length
for i in 1..count
needle += "#{i}"
end
hash.each do |word|
if word[1].squeeze == needle
puts "this word is \"#{word[0]}\""
flag = true
end
end
if !flag
puts "There no such word"
end
How this task can be solved maybe more prettily? I'm interested in Ruby library methods. A simple solution, like character-by-character cycle I already know.
For example, with input like:
lorem ipsum dolor and another lorem! sit amet lorem? and another lorem.
The output will be:
this word is "lorem"
You could do this (I modified your example slightly):
str = "a lorem ipsum lorem dolor sit amet. a tut toje est lorem! a i tuta toje lorem?"
str.split(/[.!?]/).map(&:split).reduce(:&)
#=> ["a", "lorem"]
We have:
d = str.split(/[.!?]/)
#=> ["a lorem ipsum lorem dolor sit amet",
# " a tut toje est lorem",
# " a i tuta toje lorem"]
e = d.map(&:split)
#=> [["a", "lorem", "ipsum", "lorem", "dolor", "sit", "amet"],
# ["a", "tut", "toje", "est", "lorem"],
# ["a", "i", "tuta", "toje", "lorem"]]
e.reduce(:&)
#=> ["a", "lorem"]
To make it case-insensitive, change str.split... to str.downcase.split....

Picking the most items from array 1, less items from array 2 etc

How to randomly and infinitely loop through the objects in these arrays according to their "importance"?
test = [
important = [
"lorem",
"ipsum"
],
kinda_important = [
"dolor",
"sit"
],
not_so_important = [
"amet",
"consectetur"
]
]
test.shuffle.each do |test|
sleep 5
puts test
end
should output ie.:
lorem
lorem
sit
ipsum
lorem
ipsum
ipsum
dolor
ipsum
sit
amet
lorem
ipsum
dolor
lorem
sit
...
where important is outputted most frequently, kinda_important less so etc.
It seems like you need to assign some probabilities here to your importance levels. Maybe redefine your data structure like this
test = {
(0..49) => [ # most important
"lorem",
"ipsum"
],
(50..79) => [ # semi important
"dolor",
"sit"
],
(80..99) => [ # least important
"amet",
"consectetur"
]
}
Then do something like this.
while true
rand = Kernel.rand(100)
test.each do |range, options|
if range.include?(rand)
puts options.sample
end
end
end
You'll have to edit those percentage chances to your desired randomness.
PS: You could make it slightly more readable by doing Kernel.rand(100) + 1 (which will generate a number between 1 and 100, rather than 0 and 99) and shifting the ranges up by one: (1..50) = 50%, (51..75) = 25%, etc. Just a thought.
You don't have a correct object for the data. You might use:
test = { important: ["lorem", "ipsum"],
kinda_important: ["dolor", "sit"],
not_so_important: ["amet", "consectetur"] }
You'll need some probabilities:
probs = { important: 0.5, kinda_important: 0.3, not_so_important: 0.2 }
We can now generate the desired random variates (for an arbritrary number of elements in hash and probs):
def deal(hash, probs, nbr)
last = 0.0
choices = probs.each_with_object({}) do |(group, prob),choices|
choices[last + prob] = group
last += prob
end
nbr.times.map do
rn = rand
hash[choices.find { |cum,ch| rn <= cum }.last].sample
end
end
deal(test, probs, 15)
#=> ["amet", "amet", "consectetur", "dolor", "lorem", "dolor", "amet",
# "sit", "sit", "lorem", "lorem", "lorem", "lorem", "ipsum", "ipsum"]
Here:
choices
#{0.5=>:important, 0.8=>:kinda_important, 1.0=>:not_so_important}
Let's try it:
n = 10_000
a = deal(test, probs, n)
a.uniq.map { |s| [s, a.count(s).to_f/n] }.sort_by(&:last).reverse.to_h
#=> {"ipsum" =>0.2541, "lorem"=>0.25,
# "dolor" =>0.1513, "sit" =>0.1457,
# "consectetur"=>0.1016, "amet" =>0.097
How about putting your code in a while loop:
while true
test.shuffle.each do |test|
puts test
end
end

Reverse a string in Ruby

How do you reverse a string in Ruby? I know about string#reverse. I'm interested in understanding how to write it in pure Ruby, preferably an in-place solution.
There's already an inplace reverse method, called "reverse!":
$ a = "abc"
$ a.reverse!
$ puts a
cba
If you want to do this manually try this (but it will probably not be multibyte-safe, eg UTF-8), and it will be slower:
class String
def reverse_inplace!
half_length = self.length / 2
half_length.times {|i| self[i], self[-i-1] = self[-i-1], self[i] }
self
end
end
This swaps every byte from the beginning with every byte from the end until both indexes meet at the center:
$ a = "abcd"
$ a.reverse_inplace!
$ puts a
dcba
Just for discussion, with that many alternates, it is good to see if there are major differences in speed/efficiency. I cleaned up the code a bit as the code showing output was repeatedly reversing the outputs.
# encoding: utf-8
require "benchmark"
reverse_proc = Proc.new { |reverse_me| reverse_me.chars.inject([]){|r,c| r.unshift c}.join }
class String
def reverse # !> method redefined; discarding old reverse
each_char.to_a.reverse.join
end
def reverse! # !> method redefined; discarding old reverse!
replace reverse
end
def reverse_inplace!
half_length = self.length / 2
half_length.times {|i| self[i], self[-i-1] = self[-i-1], self[i] }
end
end
def reverse(a)
(0...(a.length/2)).each {|i| a[i], a[a.length-i-1]=a[a.length-i-1], a[i]}
return a
end
def reverse_string(string) # method reverse_string with parameter 'string'
loop = string.length # int loop is equal to the string's length
word = '' # this is what we will use to output the reversed word
while loop > 0 # while loop is greater than 0, subtract loop by 1 and add the string's index of loop to 'word'
loop -= 1 # subtract 1 from loop
word += string[loop] # add the index with the int loop to word
end # end while loop
return word # return the reversed word
end # end the method
lorum = <<EOT
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent quis magna eu
lacus pulvinar vestibulum ut ac ante. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Suspendisse et pretium orci. Phasellus congue iaculis
sollicitudin. Morbi in sapien mi, eget faucibus ipsum. Praesent pulvinar nibh
vitae sapien congue scelerisque. Aliquam sed aliquet velit. Praesent vulputate
facilisis dolor id ultricies. Phasellus ipsum justo, eleifend vel pretium nec,
pulvinar a justo. Phasellus erat velit, porta sit amet molestie non,
pellentesque a urna. Etiam at arcu lorem, non gravida leo. Suspendisse eu leo
nibh. Mauris ut diam eu lorem fringilla commodo. Aliquam at augue velit, id
viverra nunc.
EOT
And the results:
RUBY_VERSION # => "1.9.2"
name = "Marc-André"; reverse_proc.call(name) # => "érdnA-craM"
name = "Marc-André"; name.reverse! # => "érdnA-craM"
name = "Marc-André"; name.chars.inject([]){|s, c| s.unshift(c)}.join # => "érdnA-craM"
name = "Marc-André"; name.reverse_inplace!; name # => "érdnA-craM"
name = "Marc-André"; reverse(name) # => "érdnA-craM"
name = "Marc-André"; reverse_string(name) # => "érdnA-craM"
n = 5_000
Benchmark.bm(7) do |x|
x.report("1:") { n.times do; reverse_proc.call(lorum); end }
x.report("2:") { n.times do; lorum.reverse!; end }
x.report("3:") { n.times do; lorum.chars.inject([]){|s, c| s.unshift(c)}.join; end }
x.report("4:") { n.times do; lorum.reverse_inplace!; end }
x.report("5:") { n.times do; reverse(lorum); end }
x.report("6:") { n.times do; reverse_string(lorum); end }
end
# >> user system total real
# >> 1: 4.540000 0.000000 4.540000 ( 4.539138)
# >> 2: 2.080000 0.010000 2.090000 ( 2.084456)
# >> 3: 4.530000 0.010000 4.540000 ( 4.532124)
# >> 4: 7.010000 0.000000 7.010000 ( 7.015833)
# >> 5: 5.660000 0.010000 5.670000 ( 5.665812)
# >> 6: 3.990000 0.030000 4.020000 ( 4.021468)
It's interesting to me that the "C" version ("reverse_string()") is the fastest pure-Ruby version. #2 ("reverse!") is fastest but it's taking advantage of the [].reverse, which is in C.
Edit by Marc-André Lafortune *
Adding an extra test case (7):
def alt_reverse(string)
word = ""
chars = string.each_char.to_a
chars.size.times{word << chars.pop}
word
end
If the string is longer (lorum *= 10, n/=10), we can see that the difference widens because some functions are in O(n^2) while others (mine :-) are O(n):
user system total real
1: 10.500000 0.030000 10.530000 ( 10.524751)
2: 0.960000 0.000000 0.960000 ( 0.954972)
3: 10.630000 0.080000 10.710000 ( 10.721388)
4: 6.210000 0.060000 6.270000 ( 6.277207)
5: 4.210000 0.070000 4.280000 ( 4.268857)
6: 10.470000 3.540000 14.010000 ( 15.012420)
7: 1.600000 0.010000 1.610000 ( 1.601219)
The Ruby equivalent of the builtin reverse could look like:
# encoding: utf-8
class String
def reverse
each_char.to_a.reverse.join
end
def reverse!
replace reverse
end
end
str = "Marc-André"
str.reverse!
str # => "érdnA-craM"
str.reverse # => "Marc-André"
Note: this assumes Ruby 1.9, or else require "backports" and set $KCODE for UTF-8.
For a solution not involving reverse, one could do:
def alt_reverse(string)
word = ""
chars = string.each_char.to_a
chars.size.times{word << chars.pop}
word
end
Note: any solution using [] to access individual letters will be of order O(n^2); to access the 1000th letter, Ruby must go through the first 999 one by one to check for multibyte characters. It is thus important to use an iterator like each_char for a solution in O(n).
Another thing to avoid is to build intermediate values of increasing length; using += instead of << in alt_reverse would also make the solution O(n^2) instead of O(n).
Building an array with unshift will also make the solution O(n^2), because it implies recopying all existing elements one index higher each time one does an unshift.
Here's one way to do it with inject and unshift:
"Hello world".chars.inject([]) { |s, c| s.unshift(c) }.join
str = "something"
reverse = ""
str.length.times do |i|
reverse.insert(i, str[-1-i].chr)
end
"abcde".chars.reduce{|s,c| c + s } # => "edcba"
Use
def reverse_string(string) # Method reverse_string with parameter 'string'.
loop = string.length # int loop is equal to the string's length.
word = '' # This is what we will use to output the reversed word.
while loop > 0 # while loop is greater than 0, subtract loop by 1 and add the string's index of loop to 'word'.
loop -= 1 # Subtract 1 from loop.
word += string[loop] # Add the index with the int loop to word.
end # End while loop.
return word # Return the reversed word.
end # End the method.
def reverse(string)
result = ""
idx = string.length - 1
while idx >= 0
result << string [idx]
idx = idx - 1
end
result
end
The solution described below. There is no need to go beyond the half of array size:
class ReverseString
def initialize(array)
#array = array
#size = #array.size
end
def process
(0...#size/2).to_a.each_with_index do |e,i|
#array[i], #array[#size-i-1] = #array[#size-i-1], #array[i]
end
#array
end
end
require 'minitest/autorun'
class ReverseStringTest < Minitest::Unit::TestCase
def test_process
assert_equal "9876543210", ReverseString.new("0123456789").process
end
end
This is the solution that made the most sense to me as a ruby beginner
def reverse(string)
reversed_string = ''
i = 0
while i < string.length
reversed_string = string[i] + reversed_string
i += 1
end
reversed_string
end
p reverse("helter skelter")
Also, using Procs ...
Proc.new {|reverse_me| reverse_me.chars.inject([]){|r,c| r.unshift c}.join}.call("The house is blue")
=> "eulb si esuoh ehT"
Proc.new would be handy here because you could then nest your reversing algorithm (and still keep things on one line). This would be handy if, for instance, you needed to reverse each word in an already-reversed sentence:
# Define your reversing algorithm
reverser = Proc.new{|rev_me| rev_me.chars.inject([]){r,c| r.unshift c}.join}
# Run it twice - first on the entire sentence, then on each word
reverser.call("The house is blue").split.map {|w| reverser.call(w)}.join(' ')
=> "blue is house The"
Hard to read one-liner,
def reverse(a)
(0...(a.length/2)).each {|i| a[i], a[a.length-i-1]=a[a.length-i-1], a[i]}
return a
end
Consider looking at how Rubinius implements the method - they implement much of the core library in Ruby itself, and I wouldn't be surprised if String#reverse and String#reverse! is implemented in Ruby.
def palindrome(string)
s = string.gsub(/\W+/,'').downcase
t = s.chars.inject([]){|a,b| a.unshift(b)}.join
return true if(s == t)
false
end
If you have sentence "The greatest victory is that" and you want to have "that is victory greatest The" you should to use this method
def solution(sentance)
sentance.split.reverse.join(" ")
end
solution("The greatest victory is that")
Here's an alternative using the xor bitwise operations:
class String
def xor_reverse
len = self.length - 1
count = 0
while (count < len)
self[count] ^= self[len]
self[len] ^= self[count]
self[count] ^= self[len]
count += 1
len -= 1
end
self
end
"foobar".xor_reverse
=> raboof
In Ruby:
name = "Hello World"; reverse_proc.call(name)
name = "Hello World"; name.reverse!
name = "Hello World"; name.chars.inject([]){|s, c| s.unshift(c)}.join
name = "Hello World"; name.reverse_inplace!;
name = "Hello World"; reverse(name)
name = "Hello World"; reverse_string(name)
I believe this would work also
def reverse(str)
string = ''
(0..str.size-1).each do |i|
string << str[str.size - 1 - i]
end
string
end
def reverse(string)
reversed_string = ""
idx = 0
while idx < string.length
reversed_string = string[idx] + reversed_string
idx += 1
end
return reversed_string
end
string = "This is my string"
string_arr = string.split('')
n = string_arr.length
new_arr = Array.new
17.times do |i|
new_arr << string_arr.values_at(n - i)
end
reversed_string = new_arr.flatten.join('')
=> "gnirts ym si sihT"
Here is a simple alternative, it first breaks the string into an array, counts the length and subtracts one(because of ruby's indexing rule for array starting from 0), creates an empty variable, then runs an iteration on the keys of the array whilst appending the value of the array length minus current array index to the empty variable created and when it reaches the zeroth(sorry for my french) value it stops. Hope this helps.
class String
def rString
arr = self.split("")
len = arr.count - 1
final = ""
arr.each_index do |i|
final += arr[len - i]
end
final
end
end
A simple classic way with n/2 complexity
str = "Hello World!";
puts str;
for i in 0..(str.length/2).to_i
mid = (str.length-1-i);
temp = str[i];
str[i] = str[aa];
str[aa] = temp;
end
puts str;
we can use inject method to make it simple:
def reverse_str(str)
(1..str.length).inject('') {|rev_str, i| rev_str.concat(str[str.length-i])}
end
Note: I have used concat instead += because concat will change the string into same reference but += will create new object
for example
str = 'sanjay' #object.id #69080
str.concat('choudhary') #object.id #69080 #look here object id is same
str += 'choudhary' #object.id #78909 #look here object id will change.

Resources