I have an array in Ruby and I would like to delete the first 10 digits in the array.
array = [1, "a", 3, "b", 2, "c", 4, "d", 5, "a", 1, "z", 7, "e", 21, "q", 30, "a", 4, "t", 7, "m", 5, 1, 2, "q", "s", "l", 13, 46, 31]
It would ideally return
['a', 'b', 'c', 'd', 'a', 'z', 'e', 'q', 0, 'a', 4, t, 7, m, 5 , 1, 2, q, s, 1, 13, 46, 31]
By removing the first 10 digits (1,3,2,4,5,1,7,2,1,3).
Note that 21(2 and 1) and 30(3 and 0) both have 2 digits
Here's what I've tried
digits = array.join().scan(/\d/).first(10).map{|s|s.to_i}
=> [1,3,2,4,5,1,7,2,1,3]
elements = array - digits
This is what I got
["a", "b", "c", "d", "a", "z", "e", 21, "q", 30, "a", "t", "m", "q", "s", "l", 13, 46, 31]
Now it looks like it took the difference instead of subtracting.
I have no idea where to go from here. and now I'm lost. Any help is appreciated.
To delete 10 numbers:
10.times.each {array.delete_at(array.index(array.select{|i| i.is_a?(Integer)}.first))}
array
To delete 10 digits:
array = [1, "a", 3, "b", 2, "c", 4, "d", 5, "a", 1, "z", 7, "e", 21, "q", 30, "a", 4, "t", 7, "m", 5, 1, 2, "q", "s", "l", 13, 46, 31]
i = 10
while (i > 0) do
x = array.select{|item| item.is_a?(Integer)}.first
if x.to_s.length > i
y = array.index(x)
array[y] = x.to_s[0, (i-1)].to_i
else
array.delete_at(array.index(x))
end
i -= x.to_s.length
end
array
Unfortunately not a one-liner:
count = 10
array.each_with_object([]) { |e, a|
if e.is_a?(Integer) && count > 0
str = e.to_s # convert integer to string
del = str.slice!(0, count) # delete up to 'count' characters
count -= del.length # subtract number of actually deleted characters
a << str.to_i unless str.empty? # append remaining characters as integer if any
else
a << e
end
}
#=> ["a", "b", "c", "d", "a", "z", "e", "q", 0, "a", 4, "t", 7, "m", 5, 1, 2, "q", "s", "l", 13, 46, 31]
I would be inclined to do it like this.
Code
def doit(array, max_nbr_to_delete)
cnt = 0
array.map do |e|
if (e.is_a? Integer) && cnt < max_nbr_to_delete
cnt += e.to_s.size
if cnt <= max_nbr_to_delete
nil
else
e.to_s[cnt-max_nbr_to_delete..-1].to_i
end
else
e
end
end.compact
end
Examples
array = [ 1, "a", 3, "b", 2, "c", 4, "d", 5, "a", 1, "z", 7, "e", 21, "q",
30, "a", 4, "t", 7, "m", 5, 1, 2, "q", "s", "l", 13, 46, 31]
doit(array, 10)
#=> ["a", "b", "c", "d", "a", "z", "e", "q", 0, "a", 4,
# "t", 7, "m", 5, 1, 2, "q", "s", "l", 13, 46, 31]
doit(array, 100)
#=> ["a", "b", "c", "d", "a", "z", "e", "q", "a", "t", "m", "q", "s", "l"]
Explanation
Each element e of the array that is not an integer is mapped to e.
For each non-negative integer n having d digits, suppose cnt is the number of digits that map has already been removed from the string. There are three possibilities:
if cnt >= max_nbr_to_delete, no more digits are to be removed, so e (itself) is returned
if cnt + d <= max_nbr_to_delete all d digits of e are to be removed, which is done by mapping e to nil and subsequently removing nil elements
if cnt < max_nbr_to_delete and cnt + d > max_nbr_to_delete, e.to_s[cnt+d-max_nbr_to_delete..-1].to_i is returned (i.e. the first cnt+d-max_nbr_to_delete digits of e are removed).
Related
local function generator()
local capital_letters = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
local low_letters = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
local numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
local Random_capital_letters = math.random(26)
local Random_low_letters = math.random(26)
local Random_numbers = math.random(10)
local length = 10
print("this is your generatet password: "..Random_capital_letters, Random_low_letters, Random_numbers[length])
math.randomseed(os.time())
end
generator()
it just gives me an error all the time, would be cool if anyone could help me!
You must initialize random seed before using math.random
It's better to use length of your tables (capital_letters, low_letters, numbers) to use math.random function to pick a value and create your password.
10 value must not be in numbers table
A loop is necessary to create your password : iterate from 1 to length and in each step, pick a random value in capital_letters, low_letters, numbers tables.
A working version adapted from your Lua code :
local function generator()
local capital_letters = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
local low_letters = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
local numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
math.randomseed(os.time())
local length = 10
local pass = ""
local choice = 0
for _ = 1, length do
choice = math.random(3)
-- Capital letters
if choice == 1 then
pass = pass .. capital_letters[math.random(#capital_letters)]
-- Low letters
elseif choice == 2 then
pass = pass .. low_letters[math.random(#low_letters)]
-- Numbers
else
pass = pass .. numbers[math.random(#numbers)]
end
end
print(pass)
end
generator()
Input: str = "stackoverflow"
Output: [19 20 1 3 11 15 22 5 18 6 12 15 23]
Do we have any method to get the position of the letters in ruby?
So that I can use something like str.chars.map { |al| al.some_method }.
str.chars = ["s", "t", "a", "c", "k", "o", "v", "e", "r", "f", "l", "o", "w"]
You can do this. I'd use String#chars which returns the ASCII numbers of each character in the string.
'abcdggg'.bytes
# => [97, 98, 99, 100, 103, 103, 103]
As you can see, the alphabet is sequential, each letter is one higher than the previous one. You can get it's position in the alphabet by taking 96 from the number.
Note that the upper-case letter is in a different position, but we can fix this using String#downcase.
To get all the alphabetical positions in a string (if it only has letters) we can write this method.
def alphabet_positions(string)
string.downcase.bytes.map{|b| b - 96}
end
This will work unexpectedly if any characters aren't letters, tho.
You can build a hash with position of a letter in an alphabet and then query this hash:
indexes = ('a'..'z').each_with_index.map{|l,i| [l, i+1]}.to_h
"stackoverflow".chars.map{|l| indexes[l]}
# => [19, 20, 1, 3, 11, 15, 22, 5, 18, 6, 12, 15, 23]
You can do that :
def position(letter)
letter.upcase.ord - 'A'.ord + 1
end
And then :
chars = ["s", "t", "a", "c", "k", "o", "v", "e", "r", "f", "l", "o", "w"]
chars.map do |char| position(char) end
=> [19, 20, 1, 3, 11, 15, 22, 5, 18, 6, 12, 15, 23]
See ord method for more information or this question
Below will give you the result you want.
str = "stackoverflow"
def conversion(str)
arr = []
str.upcase.gsub(/[A-Z]/){|m| arr << m.ord-64}
return arr
end
It is better to use each_char than chars because the latter creates an array that is immediately thrown out.
str.each_char.map{|al| al.ord - ?a.ord + 1}
# => [19, 20, 1, 3, 11, 15, 22, 5, 18, 6, 12, 15, 23]
I have a complex problem of string manipulation at hand.
I have a string in which I will have cycles, as well as recurrences which I need to identify and list down.
'abcabcabcabcabcdkkabclilabcoabcdieabcdowabcdppabzabx'
Following are the possible patterns ->
Actual indexes not used
abc -> 0,3,6,9,12,15,17, ..... (occurence index for recurring string),
0,3,6,9 (unique_occurence index for recurring string, 12, 15, 17
disqualified as there abc was a part of longer repeating substring)
abcd -> 12, 15, 17 (occurence index for recurring string), 12, 15, 17
(unique occurence index for recurring string)
bcda -> 13, 16, 18.. (occurence index for recurring string), (unique occurence index for recurring string) as it is an overlap for
the string abcd Hence it is something not required ab ->
0,3,6,9,12,15,17, 25, 27 ...(occurence index for recurring string),
25, 27(unique occurence index for recurring string). .....
I want to find all unique recurring occurences/recurrences, i.e. All Unique, Non-Overlapping values of recurring string. As mentioned above. And the input string may contain,
ALL cyclic patterns(abcabcabcdefdefdeflkjlkjlkj => abc, def, lkj are recurrences in cycle, but bc, ab, bcab are not expected as they are outcomes of false positives)
OR
Separately recurring patterns(abcxabcdabcm => abc is recurrence but not cycle, i.e. they are not adjecent)
Or
A mix of both(abcabcabcabcabclkabcdokabcdhuabcd => abc is a cyclic recurrence, and abcd is a non cyclic recurrence and we need to find both -> only abcd, abc are recurring, not bc, ab, bcda, etc)
Can someone propose a solution algo for this problem statement. I am trying using suffix_arrays which is not finding overlapping results as well.
A hash is constructed whose keys consist of all unique substrings of a given string that appear at least twice in the string (not overlapping) and, for each key, the value is an array of all offsets into the string where the value of the key (a substring) begins.
Code
def recurring_substrings(str)
arr = str.chars
(1..str.size/2).each_with_object({}) do |n,h|
arr.each_cons(n).map { |b| b.join }.uniq.each do |s|
str.scan(Regexp.new(s)) { (h[s] ||= []) << Regexp.last_match.begin(0) }
end
end.reject { |_,v| v.size == 1 }
end
Examples
recurring_substrings 'abjkabrjkab'
#=> {"a"=>[0, 4, 9], "b"=>[1, 5, 10], "j"=>[2, 7], "k"=>[3, 8], "ab"=>[0, 4, 9],
# "jk"=>[2, 7], "ka"=>[3, 8], "jka"=>[2, 7], "kab"=>[3, 8], "jkab"=>[2, 7]}
recurring_substrings "abcabcabcabcabcdkkabclilabcoabcdieabcdowabcdppabzabx"
#=> {"a"=>[0, 3, 6, 9, 12, 18, 24, 28, 34, 40, 46, 49],
# "b"=>[1, 4, 7, 10, 13, 19, 25, 29, 35, 41, 47, 50],
# "c"=>[2, 5, 8, 11, 14, 20, 26, 30, 36, 42], "d"=>[15, 31, 37, 43],
# "k"=>[16, 17], "l"=>[21, 23], "i"=>[22, 32], "o"=>[27, 38], "p"=>[44, 45],
# "ab"=>[0, 3, 6, 9, 12, 18, 24, 28, 34, 40, 46, 49],
# "bc"=>[1, 4, 7, 10, 13, 19, 25, 29, 35, 41], "ca"=>[2, 5, 8, 11],
# "cd"=>[14, 30, 36, 42],
# "abc"=>[0, 3, 6, 9, 12, 18, 24, 28, 34, 40], "bca"=>[1, 4, 7, 10],
# "cab"=>[2, 5, 8, 11], "bcd"=>[13, 29, 35, 41],
# "abca"=>[0, 6], "bcab"=>[1, 7], "cabc"=>[2, 8], "abcd"=>[12, 28, 34, 40],
# "abcab"=>[0, 6], "bcabc"=>[1, 7], "cabca"=>[2, 8],
# "abcabc"=>[0, 6], "bcabca"=>[1, 7], "cabcab"=>[2, 8]}
Explanation
For the first example above, the steps are as follows.
str = 'abjkabrjkab'
arr = str.chars
#=> ["a", "b", "j", "k", "a", "b", "r", "j", "k", "a", "b"]
q = str.size/2 # max size for string to repeat at least once
#=> 5
b = (1..q).each_with_object({})
#=> #<Enumerator: 1..5:each_with_object({})>
We can see which elements will be generated by this enumerator by converting it to an array. (I will do this a few more times below.)
b.to_a
#=> [[1, {}], [2, {}], [3, {}], [4, {}], [5, {}]]
The empty hashes will be built up as calculations progress.
Next pass the first element to the block and set the block variables to it using parallel assignment (sometimes called multiple assignment).
n,h = b.next
#=> [1, {}]
n #=> 1
h #=> {}
c = arr.each_cons(n)
#=> #<Enumerator: ["a", "b", "j", "k", "a", "b", "r", "j", "k", "a", "b"]:each_cons(1)>
c is an array of all substrings of length 1. At the next iteration it will be an array of all substrings of length 2 and so on. See Emumerable#each_cons.
c.to_a # Let's see which elements will be generated.
#=> [["a"], ["b"], ["j"], ["k"], ["a"], ["b"], ["r"], ["j"], ["k"], ["a"], ["b"]]
d = c.map { |b| b.join }
#=> ["a", "b", "j", "k", "a", "b", "r", "j", "k", "a", "b"]
e = d.uniq
#=> ["a", "b", "j", "k", "r"]
At the next iteration this will be
r = arr.each_cons(2)
#=> #<Enumerator: ["a", "b", "j", "k", "a", "b", "r", "j", "k", "a", "b"]:
# each_cons(2)>
r.to_a
#=> [["a", "b"], ["b", "j"], ["j", "k"], ["k", "a"], ["a", "b"],
# ["b", "r"], ["r", "j"], ["j", "k"], ["k", "a"], ["a", "b"]]
s = r.map { |b| b.join }
#=> ["ab", "bj", "jk", "ka", "ab", "br", "rj", "jk", "ka", "ab"]
s.uniq
#=> ["ab", "bj", "jk", "ka", "br", "rj"]
Continuing,
f = e.each
#=> #<Enumerator: ["a", "b", "j", "k", "r"]:each>
f.to_a # Let's see which elements will be generated.
#=> ["a", "b", "j", "k", "r"]
s = f.next
#=> "a"
r = (Regexp.new(s))
#=> /a/
str.scan(r) { (h[s] ||= []) << Regexp.last_match.begin(0) }
If h does not yet have a key s, h[s] #=> nil. h[s] ||= [], which expands to h[s] = h[s] || [], converts h[s] to an empty array before executing h[s] << Regexp.last_match.begin(0). That is, h[s] = h[s] || [] #=> nil || [] #=> [].
Within the block the MatchData object is retrieved with the class method Regexp::last_match. (Alternatively, one could substitute the global variable $~ for Regexp.last_match. For details, search for "special global variables" at Regexp.) MatchData#begin returns the index of str at which the current match begins.
Now
h #=> {"a"=>[0, 4, 9]}
The remaining calculations are similar, adding key-value pairs to h until the has given in the example has been constructed.
For further processing after #CarySwoveland's excellent answer :
def ignore_smaller_substrings(hash)
found_indices = []
new_hash = {}
hash.sort_by{|s,_| [-s.size,s]}.each{|s,indices|
indices -= found_indices
found_indices |= indices
new_hash[s]=indices unless indices.empty?
}
new_hash
end
pp ignore_smaller_substrings(recurring_substrings('abcabcabcabcabcdkkabclilabcoabcdieabcdowabcdppabzabx'))
Hash is sorted by decreasing string length (and then alphabetically), and indices are only allowed to appear once.
It outputs
{"abcabc"=>[0, 6],
"bcabca"=>[1, 7],
"cabcab"=>[2, 8],
"abcd"=>[12, 28, 34, 40],
"abc"=>[3, 9, 18, 24],
"bca"=>[4, 10],
"bcd"=>[13, 29, 35, 41],
"cab"=>[5, 11],
"ab"=>[46, 49],
"bc"=>[19, 25],
"cd"=>[14, 30, 36, 42],
"b"=>[47, 50],
"c"=>[20, 26],
"d"=>[15, 31, 37, 43],
"i"=>[22, 32],
"k"=>[16, 17],
"l"=>[21, 23],
"o"=>[27, 38],
"p"=>[44, 45]}
It doesn't answer the question exactly, but it comes a bit closer.
I'm having an issue that I can't seem to find documented or explained anywhere so I'm hoping someone here can help me out. I've verified the unexpected behavior on three versions of Ruby, all 2.1+, and verified that it doesn't happen on an earlier version (though it's through tryruby.org and I don't know which version they're using). Anyway, for the question I'll just post some code with results and hopefully someone can help me debug it.
arr = %w( r a c e c a r ) #=> ["r","a","c","e","c","a","r"]
arr.select { |c| arr.count(c).odd? } #=> ["e"]
arr.select! { |c| arr.count(c).odd? } #=> ["e","r"] <<<<<<<<<<<<<<< ??????
I think the confusing part for me is clearly marked and if anyone can explain if this is a bug or if there's some logic to it, I'd greatly appreciate it. Thanks!
You're modifying the array while you're read from it while you iterate over it. I'm not sure the result is defined behavior. The algorithm isn't required to keep the object in any kind of sane state while it's running.
Some debug printing during the iteration shows why your particular result happens:
irb(main):005:0> x
=> ["r", "a", "c", "e", "c", "a", "r"]
irb(main):006:0> x.select! { |c| p x; x.count(c).odd? }
["r", "a", "c", "e", "c", "a", "r"]
["r", "a", "c", "e", "c", "a", "r"]
["r", "a", "c", "e", "c", "a", "r"]
["r", "a", "c", "e", "c", "a", "r"] # "e" is kept...
["e", "a", "c", "e", "c", "a", "r"] # ... and moved to the start of the array
["e", "a", "c", "e", "c", "a", "r"]
["e", "a", "c", "e", "c", "a", "r"] # now "r" is kept
=> ["e", "r"]
You can see by the final iteration, there is only one r, and that the e has been moved to the front of the array. Presumably the algorithm modifies the array in-place, moving matched elements to the front, overwriting elements that have already failed your test. It keeps track of how many elements are matched and moved, and then truncates the array down to that many elements.
So, instead, use select.
A longer example that matches more elements makes the problem a little clearer:
irb(main):001:0> nums = (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):002:0> nums.select! { |i| p nums; i.even? }
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 8, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 8, 5, 6, 7, 8, 9, 10]
=> [2, 4, 6, 8, 10]
You can see that it does indeed move matched elements to the front of the array, overwriting non-matched elements, and then truncate the array.
Just to give you some other ways of accomplishing what you're doing:
arr = %w( r a c e c a r )
arr.group_by{ |c| arr.count(c).odd? }
# => {false=>["r", "a", "c", "c", "a", "r"], true=>["e"]}
arr.group_by{ |c| arr.count(c).odd? }.values
# => [["r", "a", "c", "c", "a", "r"], ["e"]]
arr.partition{ |c| arr.count(c).odd? }
# => [["e"], ["r", "a", "c", "c", "a", "r"]]
And if you want more readable keys:
arr.group_by{ |c| arr.count(c).odd? ? :odd : :even }
# => {:even=>["r", "a", "c", "c", "a", "r"], :odd=>["e"]}
partition and group_by are basic building blocks for separating elements in an array into some sort of grouping, so it is good to be familiar with them.
Lets say you have a string
initial_message = "My dear cousin bill!"
I put this string of N characters in an array of hashes (where each letter is the key and the value is A = 0 , B = 1, C = 2.. etc).
hsh_letter_values = Hash[('a'..'z').zip (0..25).to_a] #Map letters to numbers in a hash
clean_message = initial_message.tr('^a-zA-Z0-9','').downcase #remove non-letters
char_map = clean_message.each_char.map { |i| { i => hsh_letter_values[i] } } #map each letter of message to corresponding number
Then I split the char_map into slices of 16.
char_split_map = char_map.each_slice(16).to_a
I want to split each 16 character slice into slices of 4, while keeping the hashes in the same order.
The outcome should look like:
[[[{"m"=>12}, {"y"=>24}, {"d"=>3}, {"e"=>4}],[{"a"=>0}, {"r"=>17}, {"c"=>2}, {"o"=>14}], [{"u"=>20}, {"s"=>18}, {"i"=>8}, {"n"=>13}], [{"b"=>1}, {"i"=>8}, {"l"=>11}, {"l"=>11}]]
I am planning on adding the values of each letter from each column to get four sums (C1,C2,C3,C4)
So for the first column it would be 12+0+20+1.
This is what I have so far http://repl.it/2cd/1.
Any help on what im doing wrong or a better way to handle this situation?
One way, starting with the message:
msg = "My dear cousin bill!"
arr = msg.downcase.gsub(/[^a-z]/,'').chars.each_slice(4).to_a
#=> [["m", "y", "d", "e"],
# ["a", "r", "c", "o"],
# ["u", "s", "i", "n"],
# ["b", "i", "l", "l"]]
4.times.map { |i| arr.reduce(0) { |t,a| t + (a[i]||?a).ord-?a.ord } }
#=> [33, 67, 24, 42]
msg = "My dearest cousin bill!"
arr = msg.downcase.gsub(/[^a-z]/,'').chars.each_slice(4).to_a
#=> [["m", "y", "d", "e"],
# ["a", "r", "e", "s"],
# ["t", "c", "o", "u"],
# ["s", "i", "n", "b"],
# ["i", "l", "l"]]
4.times.map { |i| arr.reduce(0) { |t,a| t + (a[i]||?a).ord-?a.ord } }
#=>[57, 62, 45, 43]
I would probably go with a slightly different approach:
initial_message = "My dear cousin bill!"
chars = initial_message.tr('^a-zA-Z0-9','').downcase.chars
char_map = ->(char) { char.ord - 97 }
results = chars.each_slice(4).each_slice(4).map do |array|
array.transpose.map {|column| column.reduce(0) {|res, letter| res + char_map[letter]} }
end
results.inspect => '[[33, 67, 24, 42]]'
This is not hitting the intermediate step you described in your question, however is probably a better way to achieve your final result.