Add the last index of an array with a unique seperator - arrays

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.)

Related

Splitting strings and re-ordering in ruby

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"

How to delete/remove the word from array which is next to another word in ruby?

Im having an array like below:
["a", " OR ", "bc cd", " NOT ", "e"]
On that how to remove the element next to NOT? I have shown static example. values in the array is dynamic. i have to find the NOT operator and i have to delete the next word of NOT in all the array.
Thanks
You can use Array#take_while:
arr.take_while { |str| str != ' NOT ' }
#=> ["a", " OR ", "bc cd"]
For
arr = ["a", " OR ", "bc cd", " NOT ", "e"]
Method 1: Takes about O(n) time complexity
new_arr = []
i = 0
while i < arr.length
break if arr[i].strip == 'NOT' # or arr[i] != ' NOT '
new_arr << arr[i]
i += 1
end
Method 2: Again O(n)
new_arr = []
arr.each do |a|
break if a.strip == 'NOT' # or arr[i] != ' NOT '
new_arr << a
end
Method 3: Takes about O(n), but since the index method has to find the index of ' NOT ' and then yield the value to form range, it would be running 2 subsequent loops.
arr[0...arr.index(' NOT ')] # NOTE 3 dots
Also, I would avoid using Method 3, as arr.index(' NOT ') would return nil if there is no ' NOT ' in array, which will lead to a bad range exception.
You can simply provide range to array after find_index
def delete_after(element, array)
if (ind = array.find_index(element)).present?
array[0...ind]
else
array
end
end
arr = ["a", " OR ", "bc cd", " NOT ", "e"]
delete_after(' NOT ', arr) # => ["a", " OR ", "bc cd", " NOT "]

How do I get the last elements of an array in Ruby?

How do I pull the values from an array like you do with .map? Here's my code:
counter = 0
ary = Array.new
puts "How many teams do you have to enter?"
hm = gets.to_i
until counter == hm do
puts "Team City"
city = gets.chomp
puts "Team Name"
team = gets.chomp
ary.push([city, team])
counter += 1
end
ary.map { |x, y|
puts "City: #{x} | Team: #{y}"
}
print "The last team entered was: "
ary.last
The end result looks like this:
City: Boston | Team: Bruins
City: Toronto | Team: Maple Leafs
The last team entered was:
=> ["Toronto", "Maple Leafs"]
But I want the last line to read
The last team entered was: Toronto Maple Leafs
How do I get my values in that line without the =>, brackets and quotes?
Basically, you question is “how to join string array elements into a single string,” and Array#join comes to the rescue:
["Toronto", "Maple Leafs"].join(' ')
#⇒ "Toronto Maple Leafs"
An alternative way with *:
puts ["Toronto", "Maple Leafs"] * ', '
#Toronto, Maple Leafs
#=> nil
But I don't think anyone uses this notation, so as recommended in another answer use join.
Use print instead of puts when you don't want a new line character at the end of the line for example when getting user input, furthermore you can also use #{variable} to print within the same line using puts:
counter = 0
ary = Array.new
print "How many teams do you have to enter? "
hm = gets.to_i
until counter == hm do
print "Team #{counter + 1} City: "
city = gets.chomp
print "Team #{counter + 1} Name: "
team = gets.chomp
ary.push([city, team])
counter += 1
end
ary.map { |x, y| puts "City: #{x} | Team: #{y}" }
puts "The last team entered was: #{ary.last.join(' ')}"
Example Usage:
How many teams do you have to enter? 2
Team 1 City: Boston
Team 1 Name: Bruins
Team 2 City: Toronto
Team 2 Name: Maple Leafs
City: Boston | Team: Bruins
City: Toronto | Team: Maple Leafs
The last team entered was: Toronto Maple Leafs
Try it here!
Try it:
team_last = ary.last
puts "The last team entered was:" + team_last[0] + team_last[1]
As per your code ary.last itself return an array so first you would need to convert it to a string by joining the two elements in the array by ary.last.join(' ') and then you will have to interpolate it with the your message string i.e "The last team entered was: #{ary.last.join(' ')}"
The last two lines of your code would change to :
print "The last team entered was: #{ary.last.join(' ')}"

Issues with the output format of hash

So I was trying to create a program that resembles a grocery list where the user puts the item and its associated cost and it would display it as a form of a list. So I created this:
arr = []
arr2 = []
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
arr << item
arr2 << cost
end
h = { arr => arr2 }
for k,v in h
puts "#{k} costs #{v}"
end
(Code is probably very inefficient, but with my limited starter knowledge it's the best I can do)
So my problem is when I try more than two items the results would display like this (Let's say I used Banana and Kiwi for item and put a random number for their costs):
["Banana", "Kiwi"] costs [2.0, 3,0]
I, however, would like it to display like this:
Banana costs $2.00
Kiwi costs $3.00
I know it probably has to do something with this line:
h = { arr => arr2 }
But I just don't know what I can change about it. I already spend hours trying to figure out how it works so if anyone can give me a hint or help me out I would appreciate it! (Also my apologies for the vague title, didn't know better on how to describe it...)
yes, you are correct. Problem is with this line h = { arr => arr2 }. This line will create a hash like h = {["Banana", "Kiwi"] => [2.0, 3,0]}.
1) You can modify your code as below if you want to use two arrays.
(0...arr.length).each do |ind|
puts "#{arr[ind]} costs $#{arr2[ind]}"
end
2) Better, you can use a hash to store the item and it's cost and then iterate over it to show the results
hash = {}
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
hash[item] = cost
end
hash.each do |k,v|
puts "#{k} costs $#{v}"
end
You are storing the item names and their costs in 2 different arrays. So, if want to keep your storage structure like that only, you will need to modify the display of result as below:
arr.each_with_index do |item, i|
puts "#{item} costs #{arr2[i]}"
end
But a better approach would be to store all the data in 1 hash instead of 2 arrays.
items = {}
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
items[item] = cost
end
items.each do |item, cost|
puts "#{item} costs #{cost}"
end
Let me know if it helps.

How to print a 2D array with fixed column width

I have an array:
animals = [
["cats", "dogs"],
["verrylongcat", "dog"],
["shortcat", "verrylongdog"],
["cat", "dog"]
]
And I would like to display it nicely. Is there an easy way to make the colums a fixed width so I get something like this:
cats dogs
verrylongcat dog
shortcat verrylongdog
cat dog
animals is just an example, my array could also have 3, or 4 columns or even more.
You are looking for String#ljust:
max_cat_size = animals.map(&:first).max_by(&:size).size
animals.each do |cat, dog|
puts "#{cat.ljust(max_cat_size)} #{dog}"
end
If you want more than one space just add the corresponding amount in the interpolation.
Assuming your array is n × m and not 2 × m:
animal_max_sizes = animals.first.size.times.map do |index|
animals.transpose[index].map(&:to_s).max_by(&:size).size
end
animals.map do |animal_line|
animal_line.each.with_index.reduce('') do |animal_line, (animal, index)|
animal_line + animal.to_s.ljust(animal_max_sizes[index].next)
end
end.each { |animal_line_stringified| puts animal_line_stringified }
Note: The to_ses are used in case your arrays contain nils, numbers, etc.
Another way to do this is with printf-style formatting. If you know you will always have exactly 2 words in each line then you can do this:
#!/usr/bin/env ruby
lines = [
' cats dogs',
' verrylongcat dog',
'shortcat verrylongdog ',
' cat dog ',
]
lines.map(&:strip).each do |line|
puts "%-14s%s" % line.split
end
Outputs:
cats dogs
verrylongcat dog
shortcat verrylongdog
cat dog
If you need to calculate the column width based on the data, then you'd have to do a little more work:
# as #ndn showed:
first_col_width = lines.map(&:split).map(&:first).max_by(&:size).size + 2
lines.map(&:strip).each do |line|
puts "%-#{first_col_width}s%s" % line.split
end
Here's another attempt for a variable numbers of columns. Given this array:
animals = [
['Cats', 'Dogs', 'Fish'],
['Mr. Tinkles', 'Buddy', 'Nemo'],
['Calico', 'Butch', 'Marlin'],
['Ginger', 'Ivy', 'Dory']
]
We can calculate the width of each column via transpose, map, length and max:
widths = animals.transpose.map { |x| x.map(&:length).max }
#=> [11, 5, 6]
Based on this, we can generate a format string that can be passed to sprintf (or its shortcut %):
row_format = widths.map { |w| "%-#{w}s" }.join(' ')
#=> "%-11s %-5s %-6s"
%s denotes a string argument, 11, 5 and 6 are our widths and - left-justifies the result.
Let's try it:
row_format % animals[0] #=> "Cats Dogs Fish "
row_format % animals[1] #=> "Mr. Tinkles Buddy Nemo "
row_format % animals[2] #=> "Calico Butch Marlin"
That looks good, we should use a loop and wrap everything it in a method:
def print_table(array)
widths = array.transpose.map { |x| x.map(&:length).max }
row_format = widths.map { |w| "%-#{w}s" }.join(' ')
array.each do |row_values|
puts row_format % row_values
end
end
print_table(animals)
Output:
Cats Dogs Fish
Mr. Tinkles Buddy Nemo
Calico Butch Marlin
Ginger Ivy Dory
More complex formatting
With a little tweaking, you can also output a MySQL style table:
def print_mysql_table(array)
widths = array.transpose.map { |x| x.map(&:length).max }
row_format = '|%s|' % widths.map { |w| " %-#{w}s " }.join('|')
separator = '+%s+' % widths.map { |w| '-' * (w+2) }.join('+')
header, *rows = array
puts separator
puts row_format % header
puts separator
rows.each do |row_values|
puts row_format % row_values
end
puts separator
end
print_mysql_table(animals)
Output:
+-------------+-------+--------+
| Cats | Dogs | Fish |
+-------------+-------+--------+
| Mr. Tinkles | Buddy | Nemo |
| Calico | Butch | Marlin |
| Ginger | Ivy | Dory |
+-------------+-------+--------+

Resources