I know I can use %w() as a shortcut to create an array. E.g. these should be equivalent:
FOO = %w(dog, cat)
BAR = ["dog", "cat"]
But when I use include to check what's included in these arrays:
FOO = %w(dog, cat)
BAR = ["dog", "cat"]
puts FOO.include? "dog" #false
puts BAR.include? "dog" #true
The first puts returns false while the second one returns true. Why is this happening?
No these arrays are not equivalent
FOO = %w(dog, cat)
BAR = ["dog", "cat"]
But these are
FOO = %w(dog cat) # note that the `%w` syntax doesn't need commas.
BAR = ["dog", "cat"]
That said, your first version does not FOO.include? "dog" because it includes "dog," and "cat".
In addition to #spickermann's answer, printing the array themselves is often more than enough to identify the issue.
Ruby has a built-in method p that prints out any object in a "representative" format, similar to Python's repr().
FOO = %w(dog, cat)
p FOO # => ["dog,", "cat"]
# ^ hey, bro?
Related
I have a class where I was using the Array#shift instance method on an instance variable. I thought I made a "copy" of my instance variable but in fact I hadn't and shift was actually changing the instance variables.
For example, before I would have expected to get ["foo", "bar", "baz"] both times given the following:
class Foo
attr_reader :arr
def initialize arr
#arr = arr
end
def some_method
foo = arr
foo.shift
end
end
foo = Foo.new %w(foo bar baz)
p foo.arr #=> ["foo", "bar", "baz"]
foo.some_method
p foo.arr #=> ["bar", "baz"]
result:
["foo", "bar", "baz"]
["bar", "baz"]
But as shown my "copy" wasn't really a copy at all. Now, I'm not sure if I should be calling what I want a "copy", "clone", "dup", "deep clone", "deep dup", "frozen clone", etc...
I was really confused about what to search for and found a bunch of crazy attempts to do what seems like "making a copy of an array".
Then I found another answer with literally one line that solved my problem:
class Foo
attr_reader :arr
def initialize arr
#arr = arr
end
def some_method
foo = [].replace arr
foo.shift
end
end
foo = Foo.new %w(foo bar baz)
p foo.arr #=> ["foo", "bar", "baz"]
foo.some_method
p foo.arr #=> ["foo", "bar", "baz"]
output:
["foo", "bar", "baz"]
["foo", "bar", "baz"]
I understand that Array#replace is an instance method being called on an instance of Array that happens to be an empty array (so for example foo = ["cats", "and", "dogs"].replace arr will still work) and it makes sense that I get a "copy" of the instance variable #arr.
But how is that different than:
foo = arr
foo = arr.clone
foo = arr.dup
foo = arr.deep_clone
Marshal.load # something something
# etc...
Or any of the other crazy combinations of dup and map and inject that I'm seeing on SO?
This is the tricky concept of mutability in ruby. In terms of core objects, this usually comes up with arrays and hashes. Strings are mutable as well, but this can be disabled with a flag at the top of the script. See What does the comment "frozen_string_literal: true" do?.
In this case, you can call dup, deep_dup, clone easily to the same effect as replace:
['some', 'array'].dup
['some', 'array'].deep_dup
['some', 'array'].clone
Marshal.load Marshal::dump(['some', 'array'])
In terms of differences, dup and clone are the same except for some nuanced details - see What's the difference between Ruby's dup and clone methods?
The difference between these and deep_dup is that deep_dup works recursively. For example if you dup a nested array, the inner array will not be cloned:
a = [[1]]
b = a.clone
b[0][0] = 2
a # => [[2]]
The same thing happens with hashes.
Marshal.load Marshal::dump <object> is a general approach to deep cloning objects, which, unlike deep_dup, is in ruby core. Marshal::dump returns a string so it can be handy in serializing objects to file.
If you want to avoid unexpected errors like this, keep a mental index of which methods have side-effects and only call those when it makes sense to. An explanation point at the end of a method name indicates that it has side effects, but others include unshift, push, concat, delete, and pop. A big part of fuctional programming is avoiding side effects. You can see https://www.sitepoint.com/functional-programming-techniques-with-ruby-part-i/
The preferred method is dup
use array.dup whenever you need to copy an array
use array.map(&:dup) whenever you need to copy a 2D array
Don't use the marshalling trick unless you really want to deep copy an entire object graph. Usually you want to copy the arrays only but not the contained elements.
I would like to be able to take an anonymous array, iterate through it and inside of the iterator block find out what the index is of the current element.
For instance, I am trying to output only every third element.
["foo", "bar", "baz", "bang", "bamph", "foobar", "Hello, Sailor!"].each do |elem|
if index_of(elem) % 3 == 0 then
puts elem
end
end
(where index_of is a nonexistent method being used as a placeholder here to demonstrate what I'm trying to do)
In theory the output should be:
foo
bang
Hello, Sailor!
This is pretty straightforward when I'm naming the array. But when it is anonymous, I can't very well refer to the array by name. I've tried using self.find_index(elem) as well as self.index(elem) but both fail with the error: NoMethodError: undefined method '(find_)index' for main:Object
What is the proper way to do this?
Use each_with_index:
arr = ["foo", "bar", "baz", "bang", "bamph", "foobar", "Hello, Sailor!"]
arr.each_with_index do |elem, index|
puts elem if index % 3 == 0
end
Another way:
arr = ["foo", "bar", "baz", "bang", "bamph", "foobar", "Hello, Sailor!"]
arr.each_slice(3) { |a| puts a.first }
#=> foo
# bang
# Hello, Sailor!
Let's say I use the gem csv.
With the following in the console I get a list of all instance methods:
require "csv"
csv = CSV.open(FILE_NAME, "a+")
csv.methods
One method I now find in the list is e.g. first.
Can I from the command line get info about this method? (Yes I know I could google for the docs.)
Pry (an IRB alternative) supports documentation browsing:
$ pry
[1] pry(main)> require 'csv'
#=> true
[2] pry(main)> csv = CSV.new('')
#=> <#CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
[3] pry(main)> show-doc csv.first
From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: first(*arg1)
Number of lines: 8
Returns the first element, or the first n elements, of the enumerable.
If the enumerable is empty, the first form returns nil, and the
second form returns an empty array.
%w[foo bar baz].first #=> "foo"
%w[foo bar baz].first(2) #=> ["foo", "bar"]
%w[foo bar baz].first(10) #=> ["foo", "bar", "baz"]
[].first #=> nil
According to the docs, you don't have to generate the documentation beforehand:
The Pry documentation system does not rely on pre-generated rdoc or
ri, instead it grabs the comments directly above the method on demand.
This results in speedier documentation retrieval and allows the Pry
system to retrieve documentation for methods that would not be picked
up by rdoc.
The simplest way is probably ri
$ ri 'CSV#first'
= CSV#first
(from ruby core)
=== Implementation from Enumerable
------------------------------------------------------------------------------
enum.first -> obj or nil
enum.first(n) -> an_array
------------------------------------------------------------------------------
Returns the first element, or the first n elements, of the enumerable. If the
enumerable is empty, the first form returns nil, and the second form returns
an empty array.
%w[foo bar baz].first #=> "foo"
%w[foo bar baz].first(2) #=> ["foo", "bar"]
%w[foo bar baz].first(10) #=> ["foo", "bar", "baz"]
[].first #=> nil
You may need to install the docs first as explained in: How to get the Ruby documentation from the command line
Say I want to get 10 inputs in loop and store it in an array. The input will be either string or line or json string.
I'm aware of Ruby's upto and gets.chomp but I'm looking for a simple and lazy technique like:
n=10
arr = []
loop(n) { arr.push getline } #Just an example to share my thought. Will not work
Don't know if this is "simple and lazy" enough:
irb> 3.times.collect { gets.chomp }
foo
bar
baz
# => ["foo", "bar", baz"]
Array.new.
Array.new(3){gets.chomp}
(1..3).map {gets.strip!}
This works nice, and is clean for noise before and after the entries.
Valid in 1.9 and 2.0.
>> (1..3).map {gets.strip!}
Hello
1
2
=> ["Hello", "1", "2"]
I'm relatively new to ruby and I'm trying to figure out the "ruby" way of extracting multiple values from a string, based on grouping in regexes. I'm using ruby 1.8 (so I don't think I have named captures).
I could just match and then assign $1,$2 - but I feel like there's got to be a more elegant way (this is ruby, after all).
I've also got something working with grep, but it seems hackish since I'm using an array and just grabbing the first element:
input="FOO: 1 BAR: 2"
foo, bar = input.grep(/FOO: (\d+) BAR: (\d+)/){[$1,$2]}[0]
p foo
p bar
I've tried searching online and browsing the ruby docs, but haven't been able to figure anything better out.
Rubys String#match method returns a MatchData object with the method captures to return an Array of captures.
>> string = "FOO: 1 BAR: 2"
=> "FOO: 1 BAR: 2"
>> string.match /FOO: (\d+) BAR: (\d+)/
=> #<MatchData "FOO: 1 BAR: 2" 1:"1" 2:"2">
>> _.captures
=> ["1", "2"]
>> foo, bar = _
=> ["1", "2"]
>> foo
=> "1"
>> bar
=> "2"
To Summarize:
foo, bar = input.match(/FOO: (\d+) BAR: (\d+)/).captures
Either:
foo, bar = string.scan(/[A-Z]+: (\d+)/).flatten
or:
foo, bar = string.match(/FOO: (\d+) BAR: (\d+)/).captures
Use scan instead:
input="FOO: 1 BAR: 2"
input.scan(/FOO: (\d+) BAR: (\d+)/) #=> [["1", "2"]]