Slice string from end in Ruby - ruby

How can I split a string into multiple substrings of equal length but from back?
For example if string is: "ABCDEFGH", then I want an array of each string of length 3 as:
["FGH", "CDE", "AB"]

I think that this does what you're asking:
> "ABCDEFGH".reverse.scan(/.{1,3}/).each { |x| x.reverse! }
=> ["FGH", "CDE", "AB"]
Here's a quick explanation:
.reverse reverses the string so that it is "HGFEDCBA" instead of "ABCDEFGH".
.scan(/.{1,3}/) converts the string into an array with each element of the array containing 3 characters (if the string isn't divisible by 3 then the last element of the array may have 1 or 2 characters).
.each { |x| x.reverse! } reverses the characters in each element of the array.
You could define a function like this:
def slice_string_from_end(s)
s.reverse.scan(/.{1,3}/).each { |x| x.reverse! }
end
Then you can use:
slice_string_from_end("ABCDEFGH")

You can accomplish this using each_slice, but you'll need to reverse the string first, and then re-reverse each individual slice:
x = "ABCDEFGH"
x.chars.reverse.each_slice(3).map(&:reverse).map(&:join)
=> ["FGH", "CDE", "AB"]
split the string into a character array (x.chars)
reverse the array (.reverse)
slice the array into sub-arrays of 3 characters (.each_slice(3))
reverse each sub-array (.map(&:reverse))
join each sub-array back into a string (.map(&:join))

"ABCDEFGH".scan(/.+?(?=(?:.{3})*\z)/) # => ["AB", "CDE", "FGH"]
"ABCDEFGH".scan(/.+?(?=(?:.{3})*\z)/).reverse # => ["FGH", "CDE", "AB"]

split and scan also work, but like #meagar points out, you need to reverse and then re-reverse the string:
"ABCDEFGH".reverse.split(/(...)/).reject(&:empty?).map(&:reverse)
# => ["FGH", "CDE", "AB"]
the scan one doesn't need to look for empty strings:
"ABCDEFGH".reverse.scan(/...?/).map(&:reverse)
# => ["FGH", "CDE", "AB"]

You could use a while loop with some slice arithmetic:
s="ABCDEFGH"
li=[]
step=3
while s.length>0
ss=s[-(step<s.length ? step : s.length)..-1]
li << ss
s.chomp!(ss)
end
Same method works with a dynamic regex:
s="ABCDEFGH"
li=[]
step=3
reg=".{1,#{step}}$"
while s.length>0
ss=s[/#{reg}/]
li << ss
s.delete_suffix!(ss)
end
Either case:
> li
=> ["FGH", "CDE", "AB"]

Related

Convert array string objects into hashes?

I have an array:
a = ["us.production => 1", "us.stats => 1", "us.stats.total_active => 1", "us.stats.inactive => 0"]
How can I modify it into a hash object? e.g.:
h = {"us.production" => 1, "us.stats" => 1, "us.stats.total_active" => 1, "us.stats.inactive" => 0}
Thank you,
If pattern you are having is proper and constant, you can try following,
h = a.map { |x| x.split(' => ') }.to_h
# => {"us.production"=>"1", "us.stats"=>"1", "us.stats.total_active"=>"1", "us.stats.inactive"=>"0"}
Instead it is better to use split(/\s*=>\s*/) instead of split(' => ')
You can split every string with String#split and then convert an array of pairs into a hash with Array#to_h:
a = ["us.production => 1", "us.stats => 1", "us.stats.total_active => 1", "us.stats.inactive => 0"]
pairs = a.map{|s| s.split(/\s*=>\s*/)}
# => [["us.production", "1"], ["us.stats", "1"], ["us.stats.total_active", "1"], ["us.stats.inactive", "0"]]
pairs.to_h
# => {"us.production"=>"1", "us.stats"=>"1", "us.stats.total_active"=>"1", "us.stats.inactive"=>"0"}
/\s*=>\s*/ is a regular expression that matches first any number of whitespaces with \s*, then => then again any nuber of whitespaces. As it's a String#split delimiter, this part of string won't be present in a string pair.
The other answers to date are incorrect because they leave the values as strings, whereas the spec is that they be integers. That can be easily corrected. One way is to change s.split(/\s*=>\s*/) in #mrzasa's answer to k,v = s.split(/\s*=>\s*/); [k,v.to_i]. Another way is to tack .transform_values(&:to_i) to the ends of the expressions given in those answers. I expect the authors of those answers either didn't notice that integers were required or intended to leave it as an exercise for the OP to do the (rather uninteresting) conversion.
To make a single pass through the array and avoid the creation of a temporary array and local variables (other than block variables), I suggest using Enumerable#each_with_object (rather than map and to_h), and use regular expressions to extract both keys and values (rather than using String#split):
a = ["us.production => 1", "us.stats=>1", "us.stats.total_active => 1"]
a.each_with_object({}) { |s,h| h[s[/.*[^ ](?= *=>)/]] = s[/\d+\z/].to_i }
#=> {"us.production"=>1, "us.stats"=>1, "us.stats.total_active"=>1}
The first regular expression reads, "match zero or more characters (.*) followed by a character that is not a space ([^ ]), provided that is followed by zero or more spaces (*) followed by the string "=>". (?= *=>) is a positive lookahead.
The second regular expression reads, "match one or more digits (\d+) at the end of the string (the anchor \z). If that string could represent a negative integer, change that regex to /-?\d+\z/ (? makes the minus sign optional).

Ruby - Sort array into sub categories

Lets say that I have an array of words, such as this:
words = ["apple", "zebra", "boat", "dog", "ape", "bingo"]
and I want to sort them alphabetically , but group them like so:
sorted = [["ape", "apple"], ["bingo", "boat"], ["dog"], ["zebra"]]
How would I be able to do this in Ruby? Help appreciated.
I'm assuming that you are trying to group by the first letter of each word. In which case you can use sort to sort the array and group_by to group by the first character of each word (as returned by chr).
words = ["apple", "zebra", "boat", "dog", "ape", "bingo"]
sorted = words.sort.group_by(&:chr).values
You could do something like this:
words.sort.chunk { |s| s[0] }.map(&:last)
This first sorts the array alphabetically (.sort), then it "chunks" together elements with the same first character (.chunk { |s| s[0] }), then it grabs the last element from each sub-array .map(&:last).
Something like this should do the trick
words = ["apple", "zebra", "boat", "dog", "ape", "bingo"]
sorted = words.sort.group_by { |s| s[0] }.map { |k,v| v }
You're going to need an array that also represents your categories. In this case, the letters of the alphabet. Below does what you're looking for using nested loops.
words = ["apple", "zebra", "boat", "dog", "ape", "bingo"]
results = []
('a'..'z').to_a.each_with_index do |letter, index|
letter_result = []
words.each do |word|
if(word[0]==letter)
letter_result.push(word)
end
end
unless letter_result.empty?
results.push(letter_result)
end
end

How split a string into an array which i want to push inside an other array

If i have this array:
array = ["1\r\ndate\r\ntext\"...", "2\r\ndate\r\ntext", "3\r\ndate\r\ntext_one\r\ntext_two."]
And I want to split in:
array = [[1, date, "text"],[2, date, "text"], [3, date, "text", "text"]]
You know...dividing that strings into array. How can I do that?
You can just map over the array and split the substrings
result = array.map { |input| input.split }
As #davenewton mentions this can be simplified to the following
array.map(&:split)
If the delimiters of the substring change then you can pass the new delimeter as an argument to split for example if you used :
result = array.map { |input| input.split(':') }

How to collect only string instances from a collection?

I have an array, which is comprising of different kind of objects. But I would like to get only the string instances. What i wrote as below :
ary = ["11",1,2,"hi",[11]]
ary.select{|e| e.instance_of? String } # => ["11", "hi"]
I am looking for an elegant way of doing this, if any.
I would do as below using Enumerable#grep :
Returns an array of every element in enum for which Pattern === element. If the optional block is supplied, each matching element is passed to it, and the block’s result is stored in the output array.
ary = ["11",1,2,"hi",[11]]
ary.grep(String) # => ["11", "hi"]
You may want to try Object#is_a? method:
ary = ["11", 1, 2, "hi", [11]]
ary.select{|e| e.is_a? String }
# Output
=> ["11", "hi"]
Can't do better than grep, but here's another:
ary.group_by(&:class)[String] # => ["11", "hi"]

A way to select maximal values from an array

This will return the first instance of the longest string in an array:
["abcd","efgh","ijk"].max_by { |x| x.length } # => "abcd"
Similarly to this, is there a nice way to get an array of all strings with the maximal length?
["abcd","efgh","ijk"].some_trick ... # => ["abcd","efgh"]
Here we go :
["abcd","efgh","ijk"].group_by(&:size).max.last #=> ["abcd","efgh"]
Explanation :
Enumerable#group_by gives a hash containing each unique size of strings contained in the array as keys, and the matching strings as values => {4=>["abcd", "efgh"], 3=>["ijk"]}
Enumerable#max applied on a Hash will give us the highest key with its matching values, in an Array like this : [key, values] => [4, ["abcd", "efgh"]]
Array#last will give us the last element of the array ... => ["abcd", "efgh"]

Resources