Splitting strings and re-ordering in ruby - arrays

I'm trying to rearrange a string that contains a full name so that the second name is displayed before the first name.
I've managed to split the string using the code below, however when I build it seems to be returning in array format ["second name", "first name"] rather than a string "second name, "first name".
Any help appreciated!
def name_shuffler(str)
name_parts = str.split(" ")
first_name, last_name = name_parts[1], name_parts[0]
end

You could just join the two parts:
def name_shuffler(str)
name_parts = str.split(" ")
[name_parts[1], name_parts[0]].join(" ")
end
name_shuffler "one two" # => "two one"
Although you're going to want to think about handling input with no spaces, or more name parts than two.

def name_shuffler(str)
str.split(" ").reverse.join(" ")
end
name_shuffler("John Doe") #=> "Doe John"

A regex way.
"John Smith".gsub(/(\w+) (\w+)/,'\2 \1') #=> "Smith John"
A rotating way.
"John Smith".split(' ').rotate.join(' ') #=> "Smith John"

Related

Ruby: filter array removing blacklisted names

Say I have an array of sentences:
sentences = ["Tom is a good person", "Jack spent some time", "Kat did something wrong"]
and I have a blacklist of names:
blacklist = ["Jack", "Kat"]
Now I need to filter sentences into an array that removes all the sentences that contain blacklisted names, so:
["Tom is a good person"]
How would I do it in Ruby?
Thanks!
sentences = ["Tom is a good person", "Jack spent some time", "Kat did something wrong",
"Kathy knows her stuff"]
blacklist = ["Jack", "Kat"]
r = /\b#{Regexp.union(blacklist)}\b/
#=> /\b(?-mix:Jack|Kat)\b/
sentences.reject { |s| s.match?(r) }
#=> ["Tom is a good person", "Kathy knows her stuff"]
Word breaks (\b) are needed in the regular expression so that "Kat" does not match the first three letters of "Kathy". One could instead write:
r = /\b#{blacklist.join('|')}\b/
#=> /\bJack|Kat\b/
You just need to reject the records
sentences.reject!{|sentence| sentence.match(blacklist.join('|'))}
You will get the required output -
["Tom is a good person"]
Docs for reject! - https://ruby-doc.org/core-2.2.0/Array.html#method-i-reject-21
reject! will update the same array, if you don't want that, you can use reject and store in a new array.
Go back to =~ :)
sentences.reject!{|sentence| !((Regexp.new(blacklist.join('|')) =~ sentence).nil?) }
Reject the sentence, if `=~' finds a match ( Code used - !nil? )
This is going to do essentially the same thing. Although ruby says, match is faster
sentences = ["Tom is a good person", "Jack spent some time", "Kat did something wrong"]
blacklist = ["Jack", "Kat"]
Program
p sentences.filter{|string|!(blacklist.map{|x|string.match?(x)}.any?)}
If you are using < Ruby 2.6 then
p sentences.select{|string|!(blacklist.map{|x|string.include?(x)}.any?)}
Result
["Tom is a good person"]

How do I combine elements in an array matching a pattern?

I have an array of strings
["123", "a", "cc", "dddd", "mi hello", "33"]
I want to join by a space consecutive elements that begin with a letter, have at least two characters, and do not contain a space. Applying that logic to the above would yield
["123", "a", "cc dddd", "mi hello", "33"]
Similarly if my array were
["mmm", "3ss", "foo", "bar", "foo", "55"]
I would want the result to be
["mm", "3ss", "foo bar foo", "55"]
How do I do this operation?
There are many ways to solve this; ruby is a highly expressive language. It would be most beneficial for you to show what you have tried so far, so that we can help debug/fix/improve your attempt.
For example, here is one possible implementation that I came up with:
def combine_words(array)
array
.chunk {|string| string.match?(/\A[a-z][a-z0-9]+\z/i) }
.flat_map {|concat, strings| concat ? strings.join(' ') : strings}
end
combine_words(["aa", "b", "cde", "f1g", "hi", "2j", "l3m", "op", "q r"])
# => ["aa", "b", "cde f1g hi", "2j", "l3m op", "q r"]
Note that I was a little unclear exactly how to interpret your requirement:
begin with a letter, have at least two characters, and do not contain a space
Can strings contain punctuation? Underscores? Utf-8 characters? I took it to mean "only a-z, A-Z or 0-9", but you may want to tweak this.
A literal interpretation of your requirement could be: /\A[[:alpha:]][^ ]+\z/, but I suspect that's not what you meant.
Explanation:
Enumerable#chunk will iterate through the array and collect terms by the block's response value. In this case, it will find sequential elements that match/don't match the required regex.
String#match? checks whether the string matches the pattern, and returns a boolean response. Note that if you were using ruby v2.3 or below, you'd have needed some workaround such as !!string.match, to force a boolean response.
Enumerable#flat_map then loops through each "result", joining the strings if necessary, and flattens the result to avoid returning any nested arrays.
Here is another, similar, solution:
def word?(string)
string.match?(/\A[a-z][a-z0-9]+\z/i)
end
def combine_words(array)
array
.chunk_while {|x, y| word?(x) && word?(y)}
.map {|group| group.join(' ')}
end
Or, here's a more "low-tech" solution - which only uses more basic language features. (I'm re-using the same word? method here):
def combine_words(array)
previous_was_word = false
result = []
array.each do |string|
if previous_was_word && word?(string)
result.last << " #{string}"
else
result << string
end
previous_was_word = word?(string)
end
result
end
You can use Enumerable#chunk.
def chunk_it(arr)
arr.chunk { |s|
(s.size > 1) && (s[0].match?(/\p{Alpha}/)) && !s.include?(' ')}.
flat_map { |tf,a| tf ? a.join(' ') : a }
end
chunk_it(["123", "a", "cc", "dddd", "mi hello", "33"])
#=> ["123", "a", "cc dddd", "mi hello", "33"]
chunk_it ["mmm", "3ss", "foo", "bar", "foo", "55"]

Add the last index of an array with a unique seperator

I am trying to get the last character of an array to join with it's own character. I'm having trouble trying to figure this out on my own I'm still unfamiliar with built in methods on ruby. Here's where I'm at so far:
def list(names)
#last = names.last
joined = names.map(&:values).flatten.join(", ")
#joined.pop
#joined << last.join(" &")
end
What I want to do is for the last index I want to join it with it's own character. I've tried doing this for hours but I keep getting errors. If anyone can point me in the right direction on this I would greatly appreciate it.
My target goal for an output would be
list([{name: 'Bart'},{name: 'Lisa'},{name: 'Garry'}])
to output:
"Bart, Lisa & Gary"
I suggest creating the string with all names separated by commas (e.g., "Bart, Lisa, Garry") and then replacing the last comma with " &". Here are two ways to do that.
Code
def list1(names)
all_commas(names).tap { |s| s[s.rindex(',')] = ' &' }
end
def list2(names)
all_commas(names).sub(/,(?=[^,]+\z)/, ' &')
end
def all_commas(names)
names.map(&:values).join(', ')
end
Example
names = [{ name: 'Bart' }, { name: 'Lisa' } , { name: 'Garry' }]
list1 names
#=> "Bart, Lisa & Garry"
list2 names
#=> "Bart, Lisa & Garry"
Explanation
The steps are as follows.
For all_commas:
a = names.map(&:values)
#=> [["Bart"], ["Lisa"], ["Garry"]]
a.join(', ')
#=> "Bart, Lisa, Garry"
For list1
s = all_commas(names)
#=> "Bart, Lisa, Garry"
i = s.rindex(',')
#=> 10
s[i] = ' &'
#=> " &"
s #=> "Bart, Lisa & Garry"
tap's block returns s
For list2
a = all_commas(names)
#=> "Bart, Lisa, Garry"
a.sub(/,(?=[^,]+\z)/, ' &')
# => "Bart, Lisa & Garry"
The regular expression, which employs a positive lookahead, reads, "match a comma, followed by one or more characters other than comma, followed by the end of the string".
Here's a solution that yields your desired output, given your input:
def list(names)
*head, tail = names.map(&:values)
[head.join(", "), tail].join(" & ")
end
Enjoy!
Here's a solution with the Oxford comma, which I prefer :)
def list(name_list)
*names, last = name_list.flat_map(&:values)
oxford_comma = names.size > 1 ? ", " : ""
names.join(", ") + oxford_comma + "& #{last}"
end
(Note: if this is Rails, Array#to_sentence does this automatically.)

Split string to array with ruby

I have the string: "how to \"split string\" to \"following array\"" (how to "split string" to "following array").
I want to get the following array:
["how", "to", "split string", "to", "following array"]
I tried split(' ') but the result is:
["how", "to", "\"split", "string\"", "to", "\"following", "array\""]
x.split('"').reject(&:empty?).flat_map do |y|
y.start_with?(' ') || y.end_with?(' ') ? y.split : y
end
Explanation:
split('"') will partition the string in a way that non-quoted strings will have a leading or trailing space and the quoted ones wouldn't.
The following flat_map will further split an individual string by space only if it falls in the non-quoted category.
Note that if there are two consecutive quoted strings, the space in between will be it's own string after the first space and will completely disappear after the second. Aka:
'foo "bar" "baz"'.split('"') # => ["foo ", "bar", " ", "baz"]
' '.split # => []
The reject(&:empty?) is needed in case we start with a quoted string as
'"foo"'.split('"') # => ["", "foo"]
With x as your string:
x.split(?").each_slice(2).flat_map{|n, q| a = n.split; (a << q if q) || a }
When you split on quotes, you know for certain that each string in the array goes: non-quoted, quoted, non-quoted, quoted, non-quoted etc...
If we group these into pairs then we get one of the following two scenarios:
[ "non-quoted", "quoted" ]
[ "non-quoted", nil ] (only ever for the last pair of an unbalanced string)
For example 1, we split nq and append q
For example 2, we split nq and discard q
i.e.: a = n.split; (a << q if q) || q
Then we join all the pairs back up (the flat part of flat_map)

RUBY: How can I print only the words that start with a certain letter from an Array?

-------So what I have so far is-----------
array = ["milk", "bread", "pizza", "eggs", "soda", "beer"]
def first_letter(x)
if x.start_with?(b)
puts x
end
end
first_letter(array)
Really confused on what I need to do. I've checked Ruby Docs and I still can't figure out how to single out just the words that start with "B" in my array and print them to the screen. Any help would be much appreciated!
What you want is basically select the elements, matching the condition.
To this end you can use Array#select:
array.select { |word| word.start_with?('b') }
#=> ["bread", "beer"]

Resources