Using String.succ with different rules? - ruby

I currently need to code an id generator and I would like to get help on how I can do it. Basically, the id has number and letter. I wanted to use succ, but it doesn't quite do what I want. Here is the order I would like to have:
[0, 1, 2, 3, ... , 8, 9, "a", "b", "c", "d", ... , "x", "y", "z", "00", "01", "02", ..., "0a", ...]
Do you think it's possible to pass an array of what come next to succ ? Basically I would just pass something like that.
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "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"]
EDIT:
Basically I want to know the next id from an id. For example, I have a34b and this would give me a34c or a329 would give me a32a.

The id you are trying to generate can be seen as a base 36 number. So we can use String#to_i and Fixnum#to_s methods' to convert base systems (between 2 and 36).
Note: I also added a String#prev method as it may make sense here; but such a method isn't provided in the standard API.
Warning: Monkey patching core classes isn't a good practice; I just posted it as the question specifically mentioned String#succ; it may be better to subclass String to create a new id type.
Credits: Idea from this answer.
class String
def succ
(self.to_i(36) + 1).to_s(36)
end
def prev
(self.to_i(36) - 1).to_s(36)
end
end
'a329'.succ # => "a32a"
"a32a".prev # => "a329"

It seems like you basically want the id to be in base 36 (which is the numbers 0-9 plus the letters a-z). To increment a string using base 36, you should do the following:
Translate the string into an integer: nextid = last_id.to_i(36)
Add one: nextid += 1
Convert back to a string: nextid = nextid.to_s(36)

Related

Regexp ignores some letters

I'm trying to solve Chasing Subs problem. I'm trying to generate that regex according to the input data. The goal is go get all substrings (including overlapped ones) with all unique letters.
I'm trying to use regexp like this:
regexp = /(?=(?<gs>.)(?<gu>[^\k<gs>])(?<gb>[^\k<gs>\k<gu>])(?<gm>[^\k<gs>\k<gu>\k<gb>])(?<ga>[^\k<gs>\k<gu>\k<gb>\k<gm>])(?<gr>[^\k<gs>\k<gu>\k<gb>\k<gm>\k<ga>])(?<gi>[^\k<gs>\k<gu>\k<gb>\k<gm>\k<ga>\k<gr>])(?<gn>[^\k<gs>\k<gu>\k<gb>\k<gm>\k<ga>\k<gr>\k<gi>])(?<ge>[^\k<gs>\k<gu>\k<gb>\k<gm>\k<ga>\k<gr>\k<gi>\k<gn>]))/
"archipelago".scan(regexp) #=> []
"archipelbgo".scan(regexp) #=> []
"brchipelbgo".scan(regexp) #=> []
"zrchipelzgo".scan(regexp) #=> [["z", "r", "c", "h", "i", "p", "e", "l", "z"]]
Why does it behave like this? Why can't it find anything with "b" and "a"? And why does it return only one (incorrect) result with "z"? What am I doing wrong?
I don't think a regular expression is the correct tool for this problem. We could do the following, however.
def substrings(str)
arr = str.chars
(1..str.size).each_with_object([]) { |n,a|
arr.each_cons(n) { |b| a << b.join if b == b.uniq } }
end
substrings("archipelago")
#=> ["a", "r", "c", "h", "i", "p", "e", "l", "a", "g", "o", "ar", "rc", "ch", "hi",
# "ip", "pe", "el", "la", "ag", "go", "arc", "rch", "chi", "hip", "ipe", "pel",
# "ela", "lag", "ago", "arch", "rchi", "chip", "hipe", "ipel", "pela", "elag",
# "lago", "archi", "rchip", "chipe", "hipel", "ipela", "pelag", "elago", "archip",
# "rchipe", "chipel", "hipela", "ipelag", "pelago", "archipe", "rchipel", "chipela",
# "hipelag", "ipelago", "archipel", "rchipela", "chipelag", "hipelago", "rchipelag",
# "chipelago", "rchipelago"]

How to convert every second element of an array into hash value [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I have a string:
string = "a#b#c#d#e#f#g#h#...#z"
I want:
{:a => "b", :c => "d", :e => "f", ...}
First I split the string by doing:
array = string.split("#")
# => [a,b,c,d,e,f.....z]
Then I got stuck. Could anybody help?
Use each_slice to process pairs of elements from the array.
If the array has an even number of elements then calling to_h on the Enumerator returned by each_slice is enough to get the desired result:
string.split('#').each_slice(2).to_h
But to_h above fails if the last slice has only one item.
A general solution uses map to make sure the last slice always contains two items (the second being nil if needed), to prevent to_h fail:
string.split('#').each_slice(2).map{|a,b| [a.to_sym, b]}.to_h
Minor improvement of axiac's answer.
string.split("#").each_slice(2).with_object({}){|(k, v), h| h[k.to_sym] = v}
This does not create temporal arrays.
result_hash = Hash[*string.split("#")]
Here I have remove z from array to make an array with odd elements..
> array = ["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"]
> Hash[ array.each_slice( 2 ).map { |e| e } ]
#=> {"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"=>nil}

How to extract each individual combination from a flat_map?

I'm fairly new to ruby and it's my first question here on stackoverflow so pardon me if I'm being a complete noob.
The code which i am working with contains this line -
puts (6..6).flat_map{|n| ('a'..'z').to_a.combination(n).map(&:join)}
What the code does is that its starts printing each of the combinations starting from "abcdef" and continues till the end (which i have never seen as it has 26^6 combinations).
Of course having an array of that size (26^6) is unimaginable hence I was wondering if there is any way by which i can get next combination in a variable, work with it, and then continue on to the next combination ?
For example I calculate the first combination as "abcdef" and store it in a variable 'combo' and use that variable somewhere and then the next combination is calculated and "abcdeg" is stored in 'combo' and hence the loop continues ?
Thanks
(6..6).flat_map { |n| ... } doesn't do much. Your code is equivalent to:
puts ('a'..'z').to_a.combination(6).map(&:join)
To process the values one by one, you can pass a block to combination:
('a'..'z').to_a.combination(6) do |combo|
puts combo.join
end
If no block is given, combination returns an Enumerator that can be iterated by calling next:
enum = ('a'..'z').to_a.combination(6)
#=> #<Enumerator: ["a", "b", "c", ..., "w", "x", "y", "z"]:combination(6)>
enum.next
#=> ["a", "b", "c", "d", "e", "f"]
enum.next
#=> ["a", "b", "c", "d", "e", "g"]
enum.next
#=> ["a", "b", "c", "d", "e", "h"]
Note that ('a'..'z').to_a.combination(6) will "only" yield 230,230 combinations:
('a'..'z').to_a.combination(6).size
#=> 230230
As opposed to 26 ^ 6 = 308,915,776. You are probably looking for repeated_permutation:
('a'..'z').to_a.repeated_permutation(6).size
#=> 308915776
Another way to iterate from "aaaaaa" to "zzzzzz" is a simple range:
('aaaaaa'..'zzzzzz').each do |combo|
puts combo
end
Or manually by calling String#succ: (this is what Range#each does under the hood)
'aaaaaa'.succ #=> "aaaaab"
'aaaaab'.succ #=> "aaaaac"
'aaaaaz'.succ #=> "aaaaba"

Combining words in a string into anagrams using ruby

I wanted to make a program in which I would be able to sort and store the characters which are anagrams into individual groups. For ex for the string:
"scream cars for four scar creams" the answer should be:
[["scream", "creams"], ["cars", "scar"], ["for"], ["four"]]
For the above I used the code:
here = self.split()
there = here.group_by { |x| x.downcase.chars.sort}.values
And I got the required answer. But when I change the code to:
here = self.split()
there = here.group_by { |x| x.downcase.chars.sort}
I get the answer:
{["a", "c", "e", "m", "r", "s"]=>["scream", "creams"], ["a", "c", "r", "s"]=>["cars", "scar"], ["f", "o", "r"]=>["for"], ["f", "o", "r", "u"]=>["four"]}
I would like to know that why it is like this now? I got to the answer using hit-and-trial method.
As commented by Yevgeniy Anfilofyev , values is a method and hence it
Returns a new array populated with the values from hash
While, if we remove the method values then we get the whole hash and not only the array of values.

Is there a one liner to destructively use `.split(//)` (e.g. turn string into array)?

So far I have:
my_array = "Foo bar na nas"
my_array.delete!(" ").downcase!
my_array = my_array.split(//).uniq
To get:
==> ["f", "o", "b", "a", "r", "n", "s"]
I can't seem to use .split!(//) like .delete! or .downcase! but I want to do all of this in one step. Is it possible?
Using my_array.delete!(" ").downcase!.split!(//) yields "': undefined method 'split!' for nil:NilClass" so I assume .split! just doesn't exist.
No. If you will read documentation you will get that destructive methods return nil when there is nothing to change, so you cannot chain them. If you want to change string to array of it's letters excluding whitespces you should rathe run:
my_array = "Foo bar na nas".downcase.gsub(/\W/, '').split(//).uniq
There also don't exist destructive method split!. Just how can it exist? Ruby is strong-typed language so you cannot change String into Array because they aren't related.
my_array.downcase.gsub(' ','').chars.uniq
Why not use split with a regular expression matching white space or nothing?
"Foo bar na nas".downcase.split(/\s*/).uniq
This returns
["f", "o", "b", "a", "r", "n", "s"]
split! does not exist because by convention methods with ! alter the object itself in ruby, and you can not coerce a string into an array because ruby is strongly typed.
"Foo bar na nas".downcase.split(//).uniq.keep_if { |item| item != " " }
#=> ["f", "o", "b", "a", "r", "n", "s"]
"Foo bar na nas t p".downcase.split(//).uniq.keep_if { |item| item != " " }
#=> ["f", "o", "b", "a", "r", "n", "s", "t", "p"]

Resources