checking that a string contains small and big letters - ruby

I want to check that in string are small and big letters
a.to_s.each_byte do |s|
if s >= 65 && s <= 90
big = true
elsif s >= 95 && s <= 122
small = true
end
end
Is it possible to write this in shortest form?

str = a.to_s
big = true if str =~ /[A-Z]/
small = true if str =~ /[a-z]/

If you stick to the English language then you can try something like this:
if a.to_s =~ /[a-z]/ and a.to_s =~ /[A-Z]/
puts 'f'
else
puts 'p'
end
But if your code needs to be able to also handle the alphabets of other languages then you should use:
if a.to_s =~ /[[:lower:]]/ and a.to_s =~ /[[:upper:]]/
puts 'f'
else
puts 'p'
end

Although I prefer the regex solutins from #axiac and #cmramseyerthis is a way your original implementementation could be made to work:
[65...90,95...122].all? do |range|
a.to_s.each_byte.any? { |byte| range.include? byte }
end
In pseudocode:
for all of the ranges, do any of the bytes fall within the range?

Here is a candidate for the "shortest form". If both lowercase and uppercase letters are present there is either an uppercase letter following a lowercase letter or vice-versa.
str.match? /\p{Ll}\p{Lu}|\p{Lu}\p{Ll}/

Related

Ruby, how to change every "s" in each word of string, except if "s" is first letter

Trying to create a simple multi-word string method that replaces some of the letters of a string (3 for "e", 0 for "o", 1 for "I", and "z" for "s" (unless "s" is first letter in word. So, "sons sams roses" will change to "sonz samz roz3z".
All of my specs pass except for not changing the first 's' of each word in a string. I can't seem to go from working with each letter in a string to each word, targeting the first letter.
class String
def leetspeak
word = self.split("")
new_word = []
word.each do |i|
if i == "e"
new_word.push(3)
elsif i == "o"
new_word.push(0)
elsif i == "I"
new_word.push(1)
elsif (i == "s") && ((word.find_index(" ") + 1) != "s")
new_word.push("z")
else
new_word.push(i)
end
end
new_word.join
end
end
On the last elsif, I thought I could target the first letter using .find_index(" ") + 1, but it doesn't work, and even if it did, it doesn't work for the very first letter in the string, if it was "s". Any help would be appreciated.
There's many ways to accomplish that. Do you require this implementation to be using if/else? Otherwise, here follows a simpler one:
def leetspeak
self
.gsub('e', '3')
.gsub('o', '0')
.gsub('I', '1')
.gsub(/(?!^)s/, 'z')
end
The /(?!^)s/ part is a Regular Expression (or regex) that means everything that's an s except if it's the first character. The (?!something) is the negative lookahead, and the ^ is the beginning of the string. The gsub method replaces globally (in the whole string) any substring which is matched by the expression, and replaces it with the second argument.
I'm sure that are better ways to do that, both in performance and in clarity, but I guess this implementation should be good enough.
UPDATE:
I had misread the question, I've fixed the regex to not match every first s in any word using the \b, which stands for word boundary.
def leetspeak
self
.gsub('e', '3')
.gsub('o', '0')
.gsub('I', '1')
.gsub(/(?!\b)s/, 'z')
end
You can use each_with_index method.
class String
def leetspeak
word = self.split("")
new_word = []
word.each_with_index do |i, index|
if i == "e"
new_word.push(3)
elsif i == "o"
new_word.push(0)
elsif i == "I"
new_word.push(1)
elsif (i == "s") && index != 0
new_word.push("z")
else
new_word.push(i)
end
end
new_word.join
end
end
"ss".leetspeak
=> "sz"
you can invoke leetspeak method for each word:
"ss roses sams".split(" ").map{|e| e.leetspeak}.join(" ")
=> "sz r0z3z samz"
I don't understand what do you want but word.find_index(" ") returns an index and you should use the index like this:
elsif (i == "s") && ((word[word.find_index(" ") + 1]) != "s")
My example:
str = "egad!, she repeated, it's VI o'clock\nI really must go!"
If I understand the correctly, here are a couple of ways that use the hash:
replacements = { "e"=>"3", "o"=>"0", "I"=>"1", "s"=>"z" }
and the regex:
r = /
[a-rt-z] # match any lower case letter other than s
| # or
(?<=\w) # match a word character in a positive lookbehind
s # match s
/ix # case indifferent and extended mode
#1
str.gsub(r) { |s| replacements[s] || s }
#=> "3gad!, sh3 r3p3at3d, it's V1 0'cl0ck\n1 r3ally muzt g0!"
#2
replacements.default_proc = ->(_,k) { k }
str.gsub(r, replacements)
#=> "3gad!, sh3 r3p3at3d, it's V1 0'cl0ck\n1 r3ally muzt g0!"

How to count the number of characters between two characters in a string

This is the question's prompt:
Write a method that takes a string and returns true if the letter
"z" appears within three letters after an "a". You may assume
that the string contains only lowercase letters.
I'm trying to use the ternary operator, and want to include the match or count methods. Any idea on how I can find the number of characters between "a" and "z" or the simplest way to solve this?
def nearby_az(string)
string.count <= 3 ? true : false
end
Regex would be a good way to solve this problem.
You can use online regex testers to experiment with different regexes, inputs and outputs.
The first solution that comes to my mind is to come up with a pattern for each possible correct input:
az
a[a-z]z
a[a-z][a-z]z
Which means:
Match the string "az"
Match a string with "a" and then a character from "a" to "z" and then a "z" character
Match a string with an "a" and then 2 characters from "a" to "z" and then a "z"
and then combine them with the 'or' operator (|)
az|a[a-z]z|a[a-z][a-z]z
Which means match on all three of those conditions.
A link to this example is here.
Doing it this way is a bit verbose so it can be improved by expressing this in a more compact way:
a[a-z]{0,2}z
This means:
Match an "a" then match a character from "a" to "z" 0, 1 or 2 times and then match a "z"
A link to this example is here
You use the method on ruby strings called match which takes in a regex object and then check the boolean return value.
Edit:
The ruby code would look something like this:
def nearby_az(string)
return string.match(/a[a-z]{0,2}z/) != nil
end
string.match() returns an object that you can query to get information about the match. If there is no match, string.match() will return nil.
!!("fjeioaeiz" =~ /a.{,2}z/) #=> true
!!("fjeioaz" =~ /a.{,2}z/) #=> true
!!("fjeioasbdz" =~ /a.{,2}z/) #=> false
Look, Ma! No regex!
def a_upto_4_z(str)
str.each_char.with_index.any? { |c,i| c == ?a && str[i+1,3].include?(?z) }
end
a_upto_4_z "rvaxxzo" #=> true
a_upto_4_z "rvaxxxzo" #=> false
a_upto_4_z "rvaxzo" #=> true
a_upto_4_z "rvazo" #=> true
a_upto_4_z "rvzao" #=> false
Edit: #Stefan makes a good point. Let's do it this way:
def mind_the_gap(str, max_gap=2)
gap = max_gap + 1 # or larger
str.each_char do |c|
case c
when ?z
return true if gap <= max_gap
when ?a
gap = 0
else
gap += 1
end
end
false
end
mind_the_gap "rvaxxzo" #=> true
mind_the_gap "rvaxxxzo" #=> false
mind_the_gap "rvaxzo" #=> true
mind_the_gap "rvazo" #=> true
mind_the_gap "rvzao" #=> false
Note it is not necessary to increment gap when c == ?z and gap > max_gap.

Display subset of array

Say I want to puts the alphabet. So I can do something like:
alphabet = ('a'..'z')
alphabet.map do |a|
puts a
end
What I want to do now is exclude the vowels.
alphabet = ('a'..'z')
vowels = ['a','e','i','o','u']
alphabet.map do |a|
puts a unless a == vowels
end
I am trying to avoid this:
alphabet = ('a'..'z')
alphabet.map do |a|
puts a unless a == 'a'
puts a unless a == 'e'
puts a unless a == 'i'
puts a unless a == 'o'
puts a unless a == 'u'
end
How do I syntactically implement the second example so that it works properly?
A Range can be expanded into an Array. Then you can subtract another array.
chars = ('a'..'z').to_a - %w( a e i o u )
chars.each do |a|
puts a
end
As a side note, don't use #map unless you really need to. Use #each if you don't care about the returning value.
You don't want equality, you want inclusion:
puts a if vowels.include? a
Also, you're using map (same as collect) which will actually return the results of the puts statements. Unless you actually need that, use each. Or find the letters that match the condition and use that collection to print the results later.
Use the Array#include? method:
puts a unless vowels.include? a
Source: http://rubydoc.info/stdlib/core/1.9.2/Array#include%3F-instance_method
You can even get rid of the loop. This preserves the original alphabet.
alphabet = ('a'..'z')
puts (alphabet.to_a - %w(a e i o u)).join('\r')
Enumerable#grep would work, too:
('a'..'z').grep(/[^aeiou]/) { |a| puts a }
Or simply
puts ('a'..'z').grep(/[^aeiou]/)

Ruby determining whether a letter is uppercase or not

The question is very simple and probably have thousand of answers, but i am looking for some magical ruby function.
Problem:
To determine whether a letter is upcase or not i.e belongs to A-Z.
Possible Solution:
array = ["A","B", ....., "Z"]
letter = "A"
is_upcase = array.include? letter
Please note that "1" is not an uppercase letter.
Is there any magical ruby function which solve the problem with less code?
You can use POSIX character classes:
/[[:lower:]]/ - Lowercase alphabetical character
/[[:upper:]]/ - Uppercase alphabetical
Example:
def which_case(letter)
case letter
when /[[:upper:]]/
:uppercase
when /[[:lower:]]/
:lowercase
else
:other
end
end
which_case('a') #=> :lowercase
which_case('ä') #=> :lowercase
which_case('A') #=> :uppercase
which_case('Ä') #=> :uppercase
which_case('1') #=> :other
Or with a simple if statement:
puts 'lowercase' if /[[:lower:]]/ =~ 'a'
#=> lowercase
Use ===
?> ('A'..'Z') === 'C'
=> true
>> ('A'..'Z') === 'c'
=> false
>> ('A'..'Z') === '1'
=> false
>> ('A'..'Z') === '&'
=> false
>> ('A'..'Z') === 'Z'
=> true
Also lacks support for umlauts, diacritcs etc. und needs ActiveSupport, but I like the syntax:
'A'.in?('A'..'Z') # => true
'a'.in?('A'..'Z') # => false
'A' =~ /[A-Z]/ #=> 0 (boolean true)
'a' =~ /[A-Z]/ #=> nil (boolean false)
def is_upcase? x
('A'..'Z').cover? x
end
Edit: .cover? is a new function in 1.9 that checks if value is in range by only checking the endpoints. In that way the computer does not need to convert it into an array and store it in memory, making it faster.
It is basically another way of writing x >= 'A' && x <= 'Z'
x >= 'A' && x <= 'Z'
There are several ways to check if the character is uppercase
# false
c = 'c'
p c=~/[A-Z]/
p c==c.upcase
p /[A-Z]/===c
p (?A..?Z)===c
p ?A<=c&&c<=?Z
p (?A..?Z).cover?c
p c=~/[[:upper:]]/
p /[[:upper:]]/===c
# true
C = 'C'
p C=~/[A-Z]/
p C==C.upcase
p /[A-Z]/===C
p (?A..?Z)===C
p ?A<=C&&C<=?Z
p (?A..?Z).cover?C
p C=~/[[:upper:]]/
p /[[:upper:]]/===C
=~ returns a nil or 0.
!!nil == false; !!0 == true.
P.S. Not all of them works in the same way
'.' == '.'.upcase => true but it's not a capital letter
If you need to check letters with diphthongs use
/[[:upper:]]/==='Ñ' => true as expected
In this case you can to add interested letters manually:
/[A-ZÑ]/==='Ñ' => true

Calling methods within methods to Titleize in Ruby

I am trying to create a titleizing method for a programming assignment, it capitalizes certain words and ignores others. It always capitalizes the first word. To this end, I made a method that finds the first word of a string, and tried to call it within the titleize method. I'm getting an error that says "warning: string literal in condition". I've tried changing the phrasing of the if loop around, but it's not fixing my error. Can anyone explain to my why my code is broken? Thanks so much for your help!
def first_word(str)
array = str.split(' ')
return array[0]
end
def titleize(str)
words = str.split
words.each do |word|
if word != first_word(str)
word.capitalize!
elsif word != 'and' or 'the'
word.capitalize!
end
words.join ' '
end
end
Change the following
elsif word != 'and' or 'the'
to
elsif word != 'and' or word != 'the'
The operator != has higher precedence than or. It means that this line
elsif word != 'and' or 'the'
is equivalent to
elsif (word != 'and') or 'the'
and not to
elsif word != ('and' or 'the')
as you probably expected. The latter equivalence should be expressed as
elsif word != 'and' or word != 'the'
but even in this case it would not make a lot of sense and it's very hard to read.
You may want to change the link to
elsif !%w(and the).include?(word)
str = 'abc'
p "hi" if str == '1' or '12'
#=> warning: string literal in condition
or
str = 'abc'
p "hi" if (str == '1' or '12')
#=> warning: string literal in condition
p "hi" if '12'
#=> warning: string literal in condition
This happened as ruby interpreter sees your code as below:
p "hi" if str == '1' or true
The second one will always evaluates to true, because '12' always exist. The warning is saying that instead of a boolean or test, you have a string literal, '12', which always evaluates to true.
So a fix is as below:
p "hi" if str == '1' or str == '12' #=> "hi"
p "hi" if ['1','12'].include? str #=> "hi"
Not sure how readable this is. But it's short!
def titleize(str)
str.capitalize.split.map do |word|
%w{and the}.include?(word.downcase) ? word : word.capitalize
end.join(' ')
end

Resources