passing array as a command argument - ruby

I am trying to pass an array to a ruby script from a command line and facing some issue.
Here is the problem:
require 'pp'
def foo(arr1, var, arr2, var2)
puts arr1.class
pp arr1
pp arr1[0]
puts arr2.class
pp arr2
pp arr2[0]
end
foo [1, 2], 3, [5, 6], 8
Here is the output:
Array
[1, 2]
1
Array
[5, 6]
5
All is fine so far. Now I change my script to accept argument from the command line:
require 'pp'
def foo(arr1,var)
puts arr1.class
pp arr1
pp arr1[0]
end
foo ARGV[0],3
Here is the output:
jruby test.rb [1, 2], 3, [5, 6], 8
String
"[1,"
91
String
"2],"
50
As you can see, the array gets passed as a string and arr[0] basically prints the ascii value.
So the question is how do I pass an array from the command line , hopefully in one line.
Also I believe this question is related to all shell invocations than just ruby ?
I am using bash shell.
Update:
Just updated the question to indicate that there can be multiple arrays at different positions

Here's a list of ways to accomplish this. Stay away from the eval-based solutions. My favorite (though I don't know ruby, but this is my favorite:
irb(main):001:0> s = "[5,3,46,6,5]"
=> "[5,3,46,6,5]"
irb(main):002:0> a = s.scan( /\d+/ )
=> ["5", "3", "46", "6", "5"]
irb(main):003:0> a.map!{ |s| s.to_i }
=> [5, 3, 46, 6, 5]

The arguments will always come in as string, you need to find a way to turn them into the format you want, in your example an array of values, followed by a single value. I suggest using trollop for this, to take the heavy lifting out of dealing with the arguments. It can accept multi-value arguments, e.g.
require 'trollop'
opts = Trollop.options do
opt :array, 'an array', type: :ints
opt :val, 'a value', type: :int
end
puts "array: #{opts[:array].inspect}"
puts "val: #{opts[:val].inspect}"
Then you can do:
$ ruby test.rb -a 1 2 -v 3
array: [1, 2]
val: 3
And extra nice:
$ ruby test.rb --help
Options:
--array, -a <i+>: an array
--val, -v <i>: a value
--help, -h: Show this message

You can use eval although you might open a security hole:
require 'pp'
def foo(arr1, var, arr2, var2)
puts arr1.class
pp arr1
pp arr1[0]
puts arr2.class
pp arr2
pp arr2[0]
end
eval("foo " + ARGV.join(" "))
Result
Array
[1, 2]
1
Array
[5, 6]
5
Hope it helps

Related

Why does .map produce a row of nils when used to enumerate over hashes?

test =
{:content=>"type_name", :content_length=>9, :array_index=>0},
{:content=>"product_id", :content_length=>10, :array_index=>1},
{:content=>"First Item", :content_length=>10, :array_index=>0},
{:content=>"1111", :content_length=>4, :array_index=>1}
pp test.map {|x| puts x} #=>
{:content=>"type_name", :content_length=>9, :array_index=>0}
{:content=>"product_id", :content_length=>10, :array_index=>1}
{:content=>"First Item", :content_length=>10, :array_index=>0}
{:content=>"1111", :content_length=>4, :array_index=>1}
[nil, nil, nil, nil]
What is the cause of that array of nils? The map works perfectly, but then it causes these nils!
The trouble is that #map is designed to transform an array into a different array. Generally, the block of #map will not have side effects. Here's a use of #map to double all the numbers in an array:
[1, 2, 3].map { |n| n * 2} # => [2, 4, 6]
If the purpose of your loop is solely to have side effects (such as printing the elements), you want #each instead:
[1, 2, 3].each { |n| puts n }
# => 1
# => 2
# => 3
In this case, we don't care about the return value of #each. All we care about is that each number gets printed.
Argh what a stupid error!
This fixes it:
test.map {|x| puts x}
I was pretty printing the puts statement, and irb, trying to be helpful, returned nil four times!

Create an array of arrays

I'm trying to create an array of arrays to be used in a JavaScript function.
Here is the format of the array that I'm trying to create:
[[1,1],[2,3],[3,6],[4,10],[5,15],[6,21]]
Here is the ruby code to create the array:
total=0
foo=[]
(1..6).each do |number|
foo.push [number, total+=number]
end
puts foo
Here is the output of puts foo:
1
1
2
3
3
6
4
10
5
15
6
21
Any ideas how to output the correctly formatted array?
If I understand that correctly, you want to output the array somewhere in a document to be interpreted as JavaScript by the browser.
When it comes to using Ruby objects in JavaScript, you can use the JSON gem.
require 'json'
#create the array
foo.to_json
should do the trick.
This also works for hashes and some other object types.
Change puts foo to foo.inspect
total=0
foo=[]
(1..6).each do |number|
foo.push [number, total+=number]
end
foo.inspect
You can use p foo to print out the array:
total=0
foo=[]
(1..6).each do |number|
foo.push [number, total+=number]
end
p foo
This prints out: [[1, 1], [2, 3], [3, 6], [4, 10], [5, 15], [6, 21]]

Why does "puts" return a blank line, but not "puts []"?

I'm going through a Ruby tutorial, and learned that the code
puts 'start'
puts
puts 'end'
will output three lines, but the following code
puts 'start'
puts []
puts 'end'
will only output two. The stated reason is that [] isn't an object (edit: "doesn't point to anything"), so puts can't do anything with it, but why is that not also true in the first case?
I tried to find an official page about puts to figure this out, and this one was no help.
The stated reason is that [] isn't an object
Stated where?
puts has a special handling for arrays. When you pass it an array, it prints each element on a new line. You pass it an array with zero elements, it prints zero lines.
puts with an array will print one line per element. No element, no lines.
EDIT: What I just said is documented in your link:
If called with an array argument, writes each element on a new line.
The link your shared, states:
If called with an array argument, writes each element on a new line.
puts []
means, you are calling puts with empty array. i.e. no elements to print. and that's what happened.
puts arr
is like
arr.each { |e| puts e }
You can do something like this by yourself:
def p(s)
if s.respond_to? 'each'
s.each { |e| p e }
else
puts s
end
end
p 'hello' # prints line with 'hello'
p [] # prints nothing
p [1, 2] # prints 2 lines with 1 and 2
Puts with no arguments has special behaviour - i.e. print new line. In all other cases, it treats all arguments as an array, and maps these arguments to strings using #to_s, and outputs each string on a new line. That's why you get no output when calling puts []. If you want to have a new line in the output, you can either call puts with no arguments (it's obvjous), or use splat operator with empty array, like this: puts *[].
You can write your own implementation of puts in order to understand things better.
def my_puts(*args)
STDOUT.write("args is #{args.inspect}\n")
if args.empty?
STDOUT.write("\n")
else
args.each { |arg| STDOUT.write("#{arg.to_s}\n") }
end
end
1.9.3p194 :039 > my_puts
args is []
=> 1
1.9.3p194 :040 > my_puts []
args is [[]]
[]
=> [[]]
1.9.3p194 :041 > my_puts *[]
args is []
=> 1
1.9.3p194 :042 > my_puts 1,2,3
args is [1, 2, 3]
1
2
3
=> [1, 2, 3]
1.9.3p194 :043 > my_puts [1,2,3]
args is [[1, 2, 3]]
[1, 2, 3]
=> [[1, 2, 3]]
1.9.3p194 :044 > my_puts *[1,2,3]
args is [1, 2, 3]
1
2
3
=> [1, 2, 3]

Why do Ι get this error when trying to scrape a reddit Json file?

I'm trying to write a Ruby program that will retrieve all the reddit usernames from a Json file. I can get it do display the list, but there is an error after the first username every time.
require 'net/http'
require 'rubygems'
require 'json'
require 'pp'
#response = Net::HTTP.get(URI.parse("http://www.reddit.com/r/AskReddit/comments/sl1nn /could_codeine_help_me_sleep_is_it_dangerous/.json"))
result = JSON.parse(#response)
comments = result[1]['data']['children'] #this is now an array of comment hashes
(0..comments.length).each do |i|
comment = comments[i]['data']
puts comment['author']
end
Although it displays the list, I also get this error:
in block in <main>': undefined method[]' for nil:NilClass (NoMethodError)
Does anyone know I can solve this?
My guess is it's the off by one error.
a = [1, 2, 3, 4, 5]
(0..a.length).map { |n| a[n] }
# => [1, 2, 3, 4, 5, nil]
This is because
(0..5).to_a
# => [0, 1, 2, 3, 4, 5]
To be non-inclusive, you should use ..., that is:
(0...comments.length).each do |i|
Better yet, since comments is an array, you can do:
comments.each do |comment|
puts comment["data"]["author"]
end

How to act differently on the first iteration in a Ruby loop?

I always use a counter to check for the first item (i==0) in a loop:
i = 0
my_array.each do |item|
if i==0
# do something with the first item
end
# common stuff
i += 1
end
Is there a more elegant way to do this (perhaps a method)?
You can do this:
my_array.each_with_index do |item, index|
if index == 0
# do something with the first item
end
# common stuff
end
Try it on ideone.
Using each_with_index, as others have described, would work fine, but for the sake of variety here is another approach.
If you want to do something specific for the first element only and something general for all elements including the first, you could do:
# do something with my_array[0] or my_array.first
my_array.each do |e|
# do the same general thing to all elements
end
But if you want to not do the general thing with the first element you could do:
# do something with my_array[0] or my_array.first
my_array.drop(1).each do |e|
# do the same general thing to all elements except the first
end
Arrays have an "each_with_index" method which is handy for this situation:
my_array.each_with_index do |item, i|
item.do_something if i==0
#common stuff
end
What fits best is depending on the situation.
Another option (if you know your array is not empty):
# treat the first element (my_array.first)
my_array.each do | item |
# do the common_stuff
end
each_with_index from Enumerable (Enumerable is already mixed in with Array, so you can call it on an array without any trouble):
irb(main):001:0> nums = (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):003:0> nums.each_with_index do |num, idx|
irb(main):004:1* if idx == 0
irb(main):005:2> puts "At index #{idx}, the number is #{num}."
irb(main):006:2> end
irb(main):007:1> end
At index 0, the number is 1.
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
If you don't need the array afterwards:
ar = %w(reversed hello world)
puts ar.shift.upcase
ar.each{|item| puts item.reverse}
#=>REVERSED
#=>olleh
#=>dlrow
Ruby's Enumerable#inject provides an argument that can be used for doing something differently on the first iteration of a loop:
> l=[1,2,3,4]
=> [1, 2, 3, 4]
> l.inject(0) {|sum, elem| sum+elem}
=> 10
The argument is not strictly necessary for common things like sums and products:
> l.inject {|sum, elem| sum+elem}
=> 10
But when you want to do something different on the first iteration, that argument might be useful to you:
> puts fruits.inject("I like to eat: ") {|acc, elem| acc << elem << " "}
I like to eat: apples pears peaches plums oranges
=> nil
Here's a solution that doesn't need to be in an immediately enclosing loop and avoids the redundancy of specifying a status placeholder more than once unless you really need to.
do_this if ($first_time_only ||= [true]).shift
Its scope matches the holder: $first_time_only will be globally once; #first_time_only will be once for the instance, and first_time_only will be once for the current scope.
If you want the first several times, etc, you can easily put [1,2,3] if you need to distinguish which of the first iterations you're in, or even something fancy [1, false, 3, 4] if you need something weird.

Resources