Remove empty value before converting string to array - ruby

ids = "1,4,5,"
ids.split(',') => ["1", " 4", " 5", " "]
ids.split(',').map(&:to_i) => [1, 4, 5, 0]
How do I remove that empty value before it becomes a zero?

You can use #scan also
ids = "1,4,5,"
ids.scan(/\d+/).map(&:to_i)
# => [1, 4, 5]

This doesn't happen in Ruby 2.2+:
ids = "1,4,5,"
ids.split(',')
# => ["1", "4", "5"]
RUBY_VERSION # => "2.2.0"
The simple thing to do is run a preflight check on your data and normalize it to what it's supposed to be, BEFORE trying to process it:
ids = "1,4,5,"
ids.chop! if ids[-1] == ','
ids # => "1,4,5"
ids.split(',')
# => ["1", "4", "5"]
You could be a bit more rigorous in the test, since the end of the line might also contain whitespace which would throw off the cleanup.
Also, you're dealing with comma-delimited data, so consider using the built in CSV class, which is designed to work with such strings.

Related

Ruby Hash: type casting

I’m trying to get a better grasp on writing in Ruby and working with Hash tables and their values.
1. Say you have a hash:
‘FOO’= {‘baz’ => [1,2,3,4,5]}
Goal: convert each value into a string in the ‘Ruby’ way.
I’ve come across multiple examples of using .each eg.
FOO.each = { |k,v| FOO[k] = v.to_s }
However this renders an array encapsulated in a string. Eg. "[1,2,3,4,5]" where it should be ["1", "2", "3", "4", "5"].
2. When type casting is performed on a Hash that’s holds an array of values, is the result a new array? Or simply a change in type of value (eg. 1 becomes “1” when .to_s is applied (say the value was placed through a each enumerator like above).
An explanation is greatly appreciated. New to Ruby.
In the each block, k and v are the key value pair. In your case, 'baz' is key and [1,2,3,4,5] is value. Since you're doing v.to_s, it converts the whole array to string and not the individual values.
You can do something like this to achieve what you want.
foo = { 'baz' => [1,2,3,4,5] }
foo.each { |k, v| foo[k] = v.map(&:to_s) }
You can use Hash#transform_values:
foo = { 'baz' => [1, 2, 3, 4, 5] }
foo.transform_values { |v| v.map(&:to_s) } #=> {"baz"=>["1", "2", "3", "4", "5"]}

Adding/Deleting numbers in an array via user input

I want to add/remove numbers in an array based on user input. Here's what I tried:
a = %w[1 2 3 4 5 6 7 8 9]
delete_list = []
puts a
puts "pick 1-9 to del"
input = gets.to_i
input << a
puts a
The last line is to check if it worked, and I get "no implicit conversion of Array into Integer". Is this because I used %w and the array isn't integer based?
a = %w[1 2 3 4 5 6 7 8 9]
a.map! {|e| e.to_i}
puts a
puts "pick 1-9 to del"
input = gets.chomp
a.delete(input)
puts a
Well, I changed it up like so. But I don't seem to be having success with the a.delete(input) command, as my array still prints out 1-9. What am I doing wrong?
To remove an element at specific position use Array#delete_at:
input = gets.to_i
a.delete_at(input - 1) # index starts from `0`
If you want to delete item not by position, but by value, use Array#delete.
input = gets.chomp # `a` contains strings; used `.chomp` instead of `.to_i`
a.delete(input)
Yes. It is because the argument to Fixnum#<< has to be an integer, not an array.
Focusing on the key lines of code:
a = %w[1 2 3 4 5 6 7 8 9]
This makes the variable "a" an array of string elements:
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
Then you set this variable:
input = gets.to_i
This gets a string from the user ("gets" - like an abbreviation of the name getStringFromUser) and then .to_i turns it to an integer.
This would have likely resulted in a "0" (if letters entered) or whatever integer was entered:
=>0 OR => #some integer
Then you tried to put an array into the integer:
input << a
Ruby tried to take the "a" array of elements (class Array) and cram it into that integer (aka: class Fixnum) variable "input". This is where you got your error - Ruby can't put an array into an integer using a method like "<<".
If you replaced the line:
input << a
With:
a << input
You'll at least get a functional result.
If the "gets" was say, input=9, then your last puts a would give you:
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", 9]
Which is an Array element that consists of a bunch of string elements and an integer element that was pushed to the end.
Now, from your puts "pick 1-9 to del", it seems like you want to delete an element from the array.
First, you'll want your array to be integers and not strings... something like:
a.map! {|e|e.to_i}
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
(if you hadn't converted the input to an integer, you could skip that last step... or oddly convert the "input" back to a string with input.to_s)
Now that "a" is an array of integers, you can delete one using the "delete" method for Arrays and telling it to delete the value of the "input" variable:
a.delete(input)
=> 9
#it returns the value you deleted.
Your last puts a would return:
=> [1, 2, 3, 4, 5, 6, 7, 8]
It's a long step-wise answer, but hopefully that helps.

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"]

Only push specific substring to array in Ruby

I have an array that I am looping through and pushing specific values to a separate array. EX:
first_array = ["Promoter: 8", "Passive: 7"]
I want to push every value that is an integer to a separate array, that would look like this in the end:
final_array = [8,7]
It would be nice for the values in the new array to be integers. I can't think of a way to push all numeric values within a string to a new array, but what would be the best option to do what I am wanting?
first_array.map{|s| s[/\d+/].to_i}
# => [8, 7]
first_array.map{|a| a.match(/\d+/)}.compact.map{|a| a[0].to_i }
Use a regex to grab the integers,
compact the blank spaces from the strings with no integers, and
convert them all to ints
And I have to add this super short but complicated one-liner solution:
a = ["Promoter: 8", "Passive: 7"]
p a.grep(/(\d+)/){$&.to_i} #=> [8,7]
Your question, as formulated, has an easy practical answer, already provided by others. But it seems to me, that your array of strings
a = ["Promoter: 8", "Passive: 7"]
envies being a Hash. So, from broader perspective, I would take freedom of converting it to a Hash first:
require 'pyper' # (type "gem install pyper" in your command line to install it)
hsh = Hash[ a.τBmm2dτ &/(\w+): *(\d+)/.method( :match ) ]
#=> {"Promoter"=>"8", "Passive"=>"7"}
# (The construction of #τBmm2dτ Pyper method will be explained in the appendix.)
Now, having your input data in a hash, you can do things with them more easily, eg.
hsh.τmbtiτ
#=> [8, 7]
APPENDIX: Explanation of the Pyper methods.
Pyper methods are similar to Lisp #car/#cdr methods in that, that a combination of
letters controls the method behavior. In the first method, #τBmm2dτ:
τ - opening and ending character
m - means #map
B - means take a block
2 - means first 3 elements of an array
d - means all elements except the first one (same meaning as in #cdr, btw.)
So, in #τBmm2dτ, Bm applies the block as follows:
x = ["Promoter: 8", "Passive: 7"].map &/(\w+): *(\d+)/.method( :match )
#=> [#<MatchData "Promoter: 8" 1:"Promoter" 2:"8">, #<MatchData "Passive: 7" 1:"Passive" 2:"7">]
# Which results in an array of 2 MatchData objects.
Then, m2d chars map (m) the MatchData objects using 2 and d chars. Character 2 gives
x = x.map { |e| e.to_a.take 3 }
#=> [["Promoter: 8", "Promoter", "8"], ["Passive: 7", "Passive", "7"]]
and d removes the first element from each:
x = x.map { |e| e.drop 1 }
#=> [["Promoter", "8"], ["Passive", "7"]]
In the secon method, #τmbtiτ, m means again #map, b means take the second element, and ti means convert it to Integer:
{"Promoter"=>"8", "Passive"=>"7"}.to_a.map { |e| Integer e[1] }
#=> [8, 7]
If the integer part of each string in (which look like members of a hash) is always preceded by at least one space, and there is no other whitespace (other than possibly at the beginning of the string), you could do this:
first_array = ["Promoter: 8", "Passive: 7"]
Hash[*first_array.map(&:split).flatten].values.map(&:to_i) # => [8,7]
map first_array => [["Promoter:", "8"], ["Passive:", "7"]]
flatten => ["Promoter:", "8", "Passive:", "7"]
convert to hash => {"Promoter:" => "8", "Passive:" => "7"}
get hash values => ["8", "7"]
convert to ints => [8, 7]
Note the need for the splat:
Hash[*["Promoter:", "8", "Passive:", "7"]]
=> Hash["Promoter:", "8", "Passive:", "7"]
=> {"Promoter:" => "8", "Passive:" => "7"}

Why doesn't array.sort work on an array created from a string in Ruby?

If I manually create an array with
array = ["2","1","3"]
array.sort will return a sorted version of the array.
But if I create an array by using
array2 = ["213".split(//)]
or
array2 = []
array2 << "213".split(//)
array2.sort will return the unsorted array.
Why doesn't this work? Are the arrays created like that somehow different, and if yes, how?
The expression "213".split(//) already returns an array, so in both cases you're actually creating an array and adding that entire thing as the first element in a new array.
["213".split(//)]
=> [["2", "1", "3"]]
array2 << "213".split(//)
=> [["2", "1", "3"]]
Notice the double brackets. Sorting this array has no effect, because it contains just one element (itself an array). You want to remove the surrounding brackets:
"213".split(//)
=> ["2", "1", "3"]
You define array2 as ["213".split(//)]. This puts an array (["2","1","3"]) inside an array. The output is:
array = ["213".split(//)]
=> [["2", "1", "3"]]
When you try and sort that, it sorts the "bigger" array: the one with one element!
This works, though:
array = "213".split(//)
=> ["2", "1", "3"]
array.sort
=> ["1", "2", "3"]
This bit of code:
array2 = ["213".split(//)]
is creating an array with one element that is an array. To sort the array that you are interested in:
array2[0].sort
But, more likely, you should remove the outer brackets to avoid creating the array of arrays. The same applies to this:
array2 = []
array2 << "213".split(//)
It creates an empty array and then adds an array element which happens to be another array.
In the first case, you're creating an array whose elements are "2", "1", and "3". In the second case, you're creating an array that contains an array whose elements are "2", "1", and "3":
ruby-1.8.7-p334 :003 > array = ["2","1","3"]
=> ["2", "1", "3"] # Notice that this is an array of three elements
ruby-1.8.7-p334 :001 > array2 = ["213".split(//)]
=> [["2", "1", "3"]] #...but this is an array of an array of three elements
ruby-1.8.7-p334 :005 > array2 = "213".split(//)
=> ["2", "1", "3"] # Remove those extra brackets and it's equivalent to the first case.
If you're read this far, you probably figured out that in the second case, you were just sorting a single-element array and seeing that single element, which was unchanged.

Resources