Get the first half of a string in ruby - ruby

I am trying to get the first half of a string. If the string length is an odd number, then round up. I am bit stuck on getting a half of a string.
def get_first_half_of_string(string)
if string.size % 2 == 0
string[1,4]
else
string[1,5]
end
end
puts get_first_half_of_string("brilliant")
This returns rilli

When calling str[start, length] you need to use 0, not 1, as the start point. For the length you should just be able to divide the length by 2, so your method body only needs string[0, (string.length.to_f / 2).ceil]
https://ruby-doc.org/core-2.2.0/String.html#method-i-5B-5D

I would do something like this:
def first_half_of_string(string)
index = (string.size.to_f / 2).ceil
string[0, index]
end
first_half_of_string("brilliant")
#=> "brill"

Maybe you could divide the string size in half and round up if it is odd:
half = (string.size/2.to_f).ceil
string[0,half]

Following previous answers, you could open String class:
class String
def first_half
self[0,(self.size/2.to_f).ceil]
end
end
So you can do:
"ruby".first_half
=> "ru"
"hello".first_half
=> "hel"

Related

Getting position from multiple characters in a string [duplicate]

I am trying to return the index's to all occurrences of a specific character in a string using Ruby. A example string is "a#asg#sdfg#d##" and the expected return is [1,5,10,12,13] when searching for # characters. The following code does the job but there must be a simpler way of doing this?
def occurances (line)
index = 0
all_index = []
line.each_byte do |x|
if x == '#'[0] then
all_index << index
end
index += 1
end
all_index
end
s = "a#asg#sdfg#d##"
a = (0 ... s.length).find_all { |i| s[i,1] == '#' }
require 'enumerator' # Needed in 1.8.6 only
"1#3#a#".enum_for(:scan,/#/).map { Regexp.last_match.begin(0) }
#=> [1, 3, 5]
ETA: This works by creating an Enumerator that uses scan(/#/) as its each method.
scan yields each occurence of the specified pattern (in this case /#/) and inside the block you can call Regexp.last_match to access the MatchData object for the match.
MatchData#begin(0) returns the index where the match begins and since we used map on the enumerator, we get an array of those indices back.
Here's a less-fancy way:
i = -1
all = []
while i = x.index('#',i+1)
all << i
end
all
In a quick speed test this was about 3.3x faster than FM's find_all method, and about 2.5x faster than sepp2k's enum_for method.
Here's a long method chain:
"a#asg#sdfg#d##".
each_char.
each_with_index.
inject([]) do |indices, (char, idx)|
indices << idx if char == "#"
indices
end
# => [1, 5, 10, 12, 13]
requires 1.8.7+
Another solution derived from FMc's answer:
s = "a#asg#sdfg#d##"
q = []
s.length.times {|i| q << i if s[i,1] == '#'}
I love that Ruby never has only one way of doing something!
Here's a solution for massive strings. I'm doing text finds on 4.5MB text strings and the other solutions grind to a halt. This takes advantage of the fact that ruby .split is very efficient compared to string comparisions.
def indices_of_matches(str, target)
cuts = (str + (target.hash.to_s.gsub(target,''))).split(target)[0..-2]
indicies = []
loc = 0
cuts.each do |cut|
loc = loc + cut.size
indicies << loc
loc = loc + target.size
end
return indicies
end
It's basically using the horsepower behind the .split method, then using the separate parts and the length of the searched string to work out locations. I've gone from 30 seconds using various methods to instantaneous on extremely large strings.
I'm sure there's a better way to do it, but:
(str + (target.hash.to_s.gsub(target,'')))
adds something to the end of the string in case the target is at the end (and the way split works), but have to also make sure that the "random" addition doesn't contain the target itself.
indices_of_matches("a#asg#sdfg#d##","#")
=> [1, 5, 10, 12, 13]

How to count the number of zeros at the start of a string

I have a string:
"0011HelloWor00ld001"
How do I count the number of zeros in the starting of the string?
For example, the above string should return 2.
I tried .match(/^[0]+/).size but it doesn't work.
.match(/^0+/) will return a MatchData object, and thus you get 1 as the result (it denotes the number of elements in the match array).
You need to get the size of the match itself. Use one of the following:
"0011HelloWor00ld001".match(/^0+/)[0].size
"0011HelloWor00ld001"[/^0+/].size
"0011HelloWor00ld001".match(/^0+/).to_s.size
You could also simply use the index method of String like
str = '0011HelloWor00ld001'
# as noted by #steenslag if the full string is zeros index will return nil
# solve by returning full string length
str.index(/[^0]/) || str.length
#=> 2
I like #Wiktor's middle answer best, but here are three more.
#1
def nbr_leading_zeros(str)
str.split(/[^0]/).first.size
end
nbr_leading_zeros '0011Hello00' #=> 2
nbr_leading_zeros '101' #=> 0
nbr_leading_zeros '000' #=> 3
#2
def nbr_leading_zeros(str)
str.each_char.take_while { |c| c=='0' }.size
end
#3
def nbr_leading_zeros(str)
str.match(/\A0*/)&.end(0).to_i
end

Ruby: Undefined method 'length' for nil:NilClass (NoMethodError)

I am attempting to write a program that takes in a string and outputs the longest word in that string. Now, I know that my code looks pretty hairy but I am pretty new to the Ruby language so please just bear with me. I don't understand any of the other explanations given regarding this issue. I am not looking for the answer. All I want is for a kind human being to please explain to me why my program halts at line 16 with the problem stated in the title of this question. Please and thank you!
# longest_word.rb
# A method that takes in a string and returns the longest word
# in the string. Assume that the string contains only
# letters and spaces. I have used the String 'split' method to
# aid me in my quest. Difficulty: easy.
def longest_word(sentence)
array = sentence.split(" ")
idx = 0
ordered_array = []
puts(array.length)
while idx <= array.length
if (array[idx].length) < (array[idx + 1].length)
short_word = array[idx]
ordered_array.push(short_word)
idx += 1
elsif array[idx].length > array[idx + 1].length
long_word = array[idx]
ordered_array.unshift(long_word)
idx += 1
else l_w = ordered_array[0]
return l_w
end
end
end
puts("\nTests for #longest_word")
puts(longest_word("hi hello goodbye"))
At some point in your while loop, you come to a state where idx is pointing to the last item in the array. At that point, asking for array[idx+1] returns nil, and NilClass does not have a method 'length'
The simple fix would be to change the while loop condition so that idx+1 is always within the array.
May I recommend a shorter solution.
words1= "This is a sentence." # The sentence
words2 = words1.split(/\W+/) # splits the words by via the space character
words3 = words2.sort_by {|x| x.length} #sort the array
words3[-1] #gets the last word
or
def longest_word(sentence)
sentence.split(/\W+/).sort_by {|x| x.length}[-1]
end

Ruby next multiple

Is there another way to write 'a'.next.next? I've looked all over and can't seem to find it.
I've tried multiplying the .next but I keep getting errors.
Well, this might not be a good idea in the case here, but if you're looking to chain a method n times in general, you can do something like this:
2.times.inject('a') { |s| s.next }
# => 'c'
20.times.inject('a') { |s| s.next }
# => 'u'
This starts with the value 'a', runs a block that calls next, then each successive result is fed back into the block.
For what it's worth, monkey-patching String can be fine for trivial scripts, but personally I'd try to look for other solutions first, like just adding a utility function to your class/module:
def repeat_next(str, n = 1)
n.times.inject(str) { |s| s.next }
end
A shortcut for your specific problem, (a.ord + 2).chr, potentially exists, although it's not the same thing.
You can just redefine String.next like this:
class String
alias_method :next1, :next
def next(n = 1)
str = self
for i in 1..n
str = str.next1
end
str
end
end
puts 'a'.next
puts 'a'.next(2)
puts 'a'.next(20)
If you're looking for a more succinct way of doing this, you could use: ('a'.ord + 2).chr. This will convert 'a' to a numerical representation (with the "ord" method), increment it by two, then converts it back to the character representation (with "chr").
You can monkey-patch the String class in ruby to add a method to do this for you:
class String
def get_nth_char(n)
current = self
while n > 0 do
current = current.next
n = n - 1
end
current
end
end
So you can do 'a'.get_nth_char(2) # => 'c'

How do I make multiple combinations with a string in ruby?

Input should be a string:
"abcd#gmail.com"
Output should be an Array of strings:
["abcd#gmail.com",
"a.bcd#gmail.com",
"ab.cd#gmail.com",
"abc.d#gmail.com",
"a.b.cd#gmail.com",
"a.bc.d#gmail.com",
"a.b.c.d#gmail.com"]
The idea: "Make every possible combination in the first string part ("abcd") with a dot. Consecutive dots are not allowed. There are no dots allowed in the beginning and in the end of the first string part ("abcd")"
This is what I've came up with so far:
text,s = "abcd".split""
i=0
def first_dot(text)
text.insert 1,"."
end
def set_next_dot(text)
i = text.rindex(".")
text.delete_at i
text.insert(i+1,".")
end
My approach was
write a function, that sets the first dot
write a function that sets the next dot
...(magic)
I do not know how to put the pieces together. Any Idea? Or perhaps a better way?
thanx in advance
edit:
I think I found the solution :)
I will post it in about one hour (it's brilliant -> truth tables, binary numbers, transposition)
...and here the solution
s = "abc"
states = s.length
possibilites = 2**states
def set_space_or_dot(value)
value.gsub("0","").gsub("1",".")
end
def fill_with_leading_zeros(val, states)
if val.length < states
"0"*(states-val.length)+val
else
val
end
end
a = Array.new(possibilites,s)
a = a.map{|x| x.split ""}
b = [*0...possibilites].map{|x| x.to_s(2).to_s}
b = b.map{|x| fill_with_leading_zeros x,states}
b = b.map{|x| x.split ""}
c = []
for i in 0 ... a.size
c[i] = (set_space_or_dot (a[i].zip b[i]).join).strip
end
Changing pduersteler answer a little bit:
possibilities = []
string = "abcd#example.com"
(string.split('#')[0].size-1).times do |pos|
possibility = string.dup
possibilities << possibility.insert(pos+1, '.')
end
How about this (probably needs a bit more fine-tuning to suit your needs):
s = "abcd"
(0..s.size-1).map do |i|
start, rest = [s[0..i], s[(i+1)..-1]]
(0..rest.size-1).map { |j| rest.dup.insert(j, '.') }.map { |s| "#{start}#{s}"}
end.flatten.compact
#=> ["a.bcd", "ab.cd", "abc.d", "ab.cd", "abc.d", "abc.d"]
An option would be to iterate n times through your string moving the dot, where n is the amount of chars minus 1. This is what you're doing right now, but without defining two methods.
Something like this:
possibilities = []
string = "abcd#example.com"
(string.split('#')[0].size-1).times do |pos|
possibilities << string.dup.insert(pos+1, '.')
end
edit
Now tested. THanks to the comments, you need to call .dup on the string before the insert. Otherwise, the dot gets inserted into the string and will stay there for each iteration causing a mess. Calling .dup onthe string will copy the string and works on the copy instead, leaving the original string untouched.

Resources