How do I replace a range of characters in Ruby? - ruby-on-rails

With Ruby, how do I replace a range of characters in a string? For instance, given teh string
hellothere
If I want to replace characters at index positions two through five inclusive with "#" to result in a string
he####here
How would I do this?

You could get a string range and replace it by setting the new character multiplied for the last index plus 1 less the first index:
def replace_in_string(str, replace, start, finish)
str[start..finish] = replace * (finish + 1 - start)
str
end
p replace_in_string 'hellothere', '#', 2, 5
# "he####here"

Related

Creating Random Strinngs in Ruby with at least one sepcial character, one digit, one upper-case, one lowercase with no characters repeated in Ruby

My task is to generate a random string with following parameters:
At least one Uppercase
At least one lower
At least one digit
No repeated chars/digits allowed ( e.g. aa not allowed, aba is allowed, Aa is allowed)
I'm able to generate a random string with 1,2,3 parameters but parameter 4 logic is missing.
inputChars = [('a'..'z'), ('A'..'Z'),(0..9)].map(&:to_a).flatten
string = (0...16).map { inputChars[rand(inputChars.length)] }.join
require 'set'
inputChars = [('a'..'z'), ('A'..'Z'),(0..9)].map(&:to_a).flatten
set_string = Set.new
loop do
break if set_string.size == 16
cr = inputChars[rand(inputChars.length)]
set_string << cr
end
output = set_string.to_a.join
i just change your map operation to loop operation and add Set data structure to store the character from random inputChars operation. Using Set will not allow same character
Let's begin by defining two constants.
CHARS_BY_TYPE = {
lower: ('a'..'z').to_a.freeze,
upper: ('A'..'Z').to_a.freeze,
digit: ('0'..'9').to_a.freeze
}.freeze
ALL = (CHARS_BY_TYPE[:lower] + CHARS_BY_TYPE[:upper] + CHARS_BY_TYPE[:digit]).freeze
#=> [["a", "b",..., "z", "A", "B",..., "Z", "0", "1",..., "9"]
I will initially build a string of a specified length by randomly selecting one character at a time from the array ALL, ensuring that no two consecutive characters are the same. There is no assurance, however, that the resulting string will contain at least one uppercase letter, one lowercase letter and one digit.
def append_random_char(last_char)
loop do
ch = ALL.sample
break ch unless ch == last_char
end
end
Our main method will begin as follows:
def random_string(str_len)
raise ArgumentError if str_len < 3
(str_len - 2).times.with_object('') { |_,s| s << append_random_char(s[-1]) }
# ...
end
For example:
s = random_string(40)
#=> "arN64kDw6ClzcNMj8WAkj1NJC2B5oFoRlcXl5S"
str_len is the required string length, 40 in the example. Observe that s contains 38 characters of which no two successive characters are equal. We will need to add 2 characters later. If the string contained no digits, for example, at least one of those two characters added (at a random location) will be a (randomly-selected) digit. If the string were shorter and contained, for example, digits only, the two characters added will be an uppercase letter and a lowercase letter.
Next we need to see if the string is lacking an uppercase letter, a lowercase letter and/or a digit. (It cannot be missing all three, as the string must contain at least three characters.)
require 'set'
def types_to_add(str)
[:lower, :upper, :digit].select do |type|
st = CHARS_BY_TYPE[type].to_set
str.each_char.none? { |ch| st.include?(ch) }
end
end
For the random string generated above we obtain:
types_to_add(s)
#=> []
meaning that the string contains at least one uppercase letter, one lowercase letter and one digit. Try this:
types_to_add(s.gsub(/\d|[A-Z]/, '')
#=> [:upper, :digit]
See Enumerable#none?. CHARS_BY_TYPE[type] is converted to a set merely to speed look-ups.
Suppose now we need to insert an uppercase letter, lowercase letter or digit to satisfy the requirement that there is at least one of each in the string. Specifically, we wish to insert a randomly-drawn character (from CHARS_BY_TYPE[:lower], CHARS_BY_TYPE[:upper] or CHARS_BY_TYPE[:digit]) at a random location in the string we are constructing, with the restriction that neither the preceding nor following character is the same character.
def insert_in_string(str, ch)
i = loop do
i = rand(str.size + 1)
next if ch == str[i]
break i if i.zero? || ch != str[i-1]
end
str.insert(i, ch)
end
For example, if we were to insert the character '0' into (a copy of) our string s (which is not needed):
insert_in_string(s.dup, '0')
#=> "arN64kDw6ClzcN0Mj8WAkj1NJC2B5oFoRlcXl5S"
s #=> "arN64kDw6ClzcNMj8WAkj1NJC2B5oFoRlcXl5S"
^
This inserts the character ch before the character in str at index i. If rand(str.size + 1) returns str.size ch is inserted after the last character of str.
Following this operation the final step is to use the method append_random_char to build the string out to the desired length.
The completed main method is as follows.
def random_string(str_len)
raise ArgumentError if str_len < 3
s = (str_len - 2).times.with_object('') { |_,s| s << append_random_char(s[-1]) }
types_to_add(s).each { |type| insert_in_string(s, CHARS_BY_TYPE[type].sample) }
(str_len - s.size).times { s << append_random_char(s[-1]) }
s
end
s = random_string(40)
#=> "PtQrVFZWUYFwiwRy3ySfAy42G1NT98J6cMVMaWeT"
s.match?(/[a-z]/)
#=> true
s.match?(/[A-Z]/)
#=> true
s.match?(/\d/)
#=> true
s.size
#=> 40
This is how I would do it (warning: Not tested. Just want to present the idea
for my algorithm). I first take a random number for the length of the resulting random string (the length will be between 4 and 16 characters). Then I determine
randomly, how many of them are upper case / lower case / digits, and based on
these decision, I generate the string, ensuring that I don't get any duplicates
in succession.
uchars=('A'..'Z').to_a
lchars=('a'..'z').to_a
dchars=('0'..'9').to_a
charmap = { u: uchars, l: lchars, d: dchars }
total_length=rand(13)+4 # Total length of string to be generated
total_u=rand(total_length-3)+1 # Total number of uchars to be generated
total_l=rand(total_length-total_u-2)+1 # Total number of lchars
total_d=total_length-total_u-total_l # Total number of digits
# Array of types to generate
chartypes=([:u]*total_u + [:l]*total_l + [:d]*total_d).shuffle
# chartypes is an array similar to [:u,:d,:d,:l,:u], where the
# symbols designate the kind of character to be generated.
# outstr : random string to be generated
outstr = charmap[chartypes.first].sample
last_char = outstr.dup
total_length.times do |index|
loop do
nextchar = charmap[chartypes[index]].sample
if nextchar != last_char
outstr << nextchar
last_char = nextchar
break
end
end
end

Trying to make function which takes string as input and returns no. of words in whole string

**It takes Input as a string such as this - 'Nice one'
And Output gives - 4,3 (which is no. Of words in sentence or string)
**
function countx(str)
local count = {}
for i = 1, string.len(str) do
s = ''
while (i<=string.len(str) and string.sub(str, i, i) ~= ' ' ) do
s = s .. string.sub(str, i, i)
i = i+1
end
if (string.len(s)>0) then
table.insert(count,string.len(s))
end
end
return table.concat(count, ',')
end
You can find a simple alternative with your new requirements:
function CountWordLength (String)
local Results = { }
local Continue = true
local Position = 1
local SpacePosition
while Continue do
SpacePosition = string.find(String, " ", Position)
if SpacePosition then
Results[#Results + 1] = SpacePosition - Position
Position = SpacePosition + 1
-- if needed to print the string
-- local SubString = String:sub(Position, SpacePosition)
-- print(SubString)
else
Continue = false
end
end
Results[#Results + 1] = #String - Position + 1
return Results
end
Results = CountWordLength('I am a boy')
for Index, Value in ipairs(Results) do
print(Value)
end
Which gives the following results:
1
2
1
3
def countLenWords(s):
s=s.split(" ")
s=map(len,s)
s=map(str,s)
s=list(s)
return s
The above functions returns a list containing number of characters in each word
s=s.split(" ") splits string with delimiter " " (space)
s=map(len,s) maps the words into length of the words in int
s=map(str,s) maps the values into string
s=list(s) converts map object to list
Short version of above function (all in one line)
def countLenWords(s):
return list(map(str,map(len,s.split(" "))))
-- Localise for performance.
local insert = table.insert
local text = 'I am a poor boy straight. I do not need sympathy'
local function word_lengths (text)
local lengths = {}
for word in text:gmatch '[%l%u]+' do
insert (lengths, word:len())
end
return lengths
end
print ('{' .. table.concat (word_lengths (text), ', ') .. '}')
gmatch returns an iterator over matches of a pattern in a string.
[%l%u]+ is a Lua regular expression (see http://lua-users.org/wiki/PatternsTutorial) matching at least one lowercase or uppercase letter:
[] is a character class: a set of characters. It matches anything inside brackets, e.g. [ab] will match both a and b,
%l is any lowercase Latin letter,
%u is any uppercase Latin letter,
+ means one or more repeats.
Therefore, text:gmatch '[%l%u]+' will return an iterator that will produce words, consisting of Latin letters, one by one, until text is over. This iterator is used in generic for (see https://www.lua.org/pil/4.3.5.html); and on any iteration word will contain a full match of the regular expression.

How do I extract numbers from a string?

How would I extract every number from a string and put them in an array?
For example the string:
"\113\115\106\111\117\41\40\105\102\109\109\112\40\42"
You can use string.gmatch like this:
local my_array = {}
local my_string = "\\113\\115\\106\\111\\117\\41\\40\\105\\102\\109\\109\\112\\40\\42"
print(my_string) --note how the string is \ followed by digits
for number in string.gmatch(my_string, "\\(%d+)") do
my_array[#my_array + 1] = tonumber(number)
print(number)
end
This will get you an table with all the numbers from your string.
The \ is escaped in my example to make it equal to the string you stated.
If i misunderstood your question and the numbers you want are from the chars then you need to do
local my_array = {}
local my_string = "\113\115\106\111\117\41\40\105\102\109\109\112\40\42"
print(my_string) --note how the string is letters
for char in string.gmatch(my_string, ".") do
my_array[#my_array + 1] = string.byte(char)
print(char, my_array[#my_array])
end

how to remove a second AMPERSAND in a string?

1.how to remove second AMPERSAND from following string?
"apple&banana&grape&apple"
2.how to split the string at second AMPERSAND for following string? I want to ignore the first AMPERSAND and split from second AMPERSAND onwards.
"apple&banana&grape&apple"
arr = "apple&banana&grape&apple".split('&')
#arr = ["apple", "banana", "grape", "apple"]
To solve first query,
arr[0..1].join('&').concat(arr[2..-1].join('&'))
# "apple&bananagrape&apple"
For second query,
[words[0..1].join('&'), words[2..-1]].flatten
#["apple&banana", "grape", "apple"]
You can use gsub with a block:
For your first case:
index_to_remove = 2
current_index = 0
my_string = "apple&banana&grape&apple"
my_string.gsub('&') { |x| current_index += 1; current_index == index_to_remove ? '' : x}
#=> "apple&bananagrape&apple"
For your second case, you can replace the second & with a unique value and split on that:
index_to_split = 2
current_index = 0
my_string = "apple&banana&grape&apple"
my_string.gsub!('&') { |x| current_index += 1; current_index == index_to_split ? '&&' : x}
my_string.split('&&')
#=> ["apple&banana", "grape&apple"]
If you split the string by the &, it's quite simple to rejoin the first two / last two and convert back into a string or array:
words = "apple&banana&grape&apple".split('&')
# First query
words[0..1].join('&') + words[2..-1].join('&')
# Giving you: "apple&bananagrape&apple"
# Second query
[words[0..1].join('&'), words[2..-1].join('&')]
# Giving you: ["apple&banana", "grape&apple"]
You could tweak this for different length strings and separators as needed.
I imagine there's a good solution using regex matchers, but I'm not too hot on them, so hope this helps in some way!

How to split string by string length and a separator?

I'm trying to split a string into 2 strings when main string is over 30 chars and separator I wanted to use is a simple space between chars(the last space between words in main string) so it won't cut words. I'm asking you guys for help because I'm not very good with patterns in Lua.
local function split(str, max_line_length)
local lines = {}
local line
str:gsub('(%s*)(%S+)',
function(spc, word)
if not line or #line + #spc + #word > max_line_length then
table.insert(lines, line)
line = word
else
line = line..spc..word
end
end
)
table.insert(lines, line)
return lines
end
local main_string = 'This is very very very very very very long string'
for _, line in ipairs(split(main_string, 20)) do
print(line)
end
-- Output
This is very very
very very very very
long string
If you just want to split the string at the last space between words, try this
s="How to split string by string length and a separator"
a,b=s:match("(.+) (.+)")
print(s)
print(a)
print(b)

Resources