Regarding the exercises in Michael Hartl's RoR Tutorial in lesson 4.3.3 (Hashes & Symbols):
"Define a hash with symbol keys corresponding to name, email, and a “password digest”, and values equal to your name, your email address, and a random string of 16 lower-case letters."
I am hoping to get some input and/or alternative & 'better' solutions to this (or at least some criticism regarding my solution).
def my_hash
a = ('a'..'z').to_a.shuffle[0..15].join
b = { name: "John", email: "johndoe#gmail.com", password: a }
return b
end
puts my_hash
(Yes I realize this is a very simple exercise and apologize if it has been asked before.)
There are many 2 improvements could be made:
Use Array#sample to get random letters (it has an advantage: the letter might in fact repeat in the password, while shuffle[0..15] will return 16 distinct letters);
Avoid redundant local variables and especially return.
Here you go:
def my_hash
{
name: "John",
email: "johndoe#gmail.com",
password: ('a'..'z').to_a.sample(16).join
}
end
puts my_hash
Bonus:
I accidentaly found the third glitch in the original code. It should probably be:
def my_hash
{
name: "Brandon",
email: "brandon.elder#gmail.com",
password: ('a'..'z').to_a.sample(16).join
}
end
:)
Related
Trying to iterate over an array using ruby and it is miserably failing,
My Array
people = [{first_name: "Gary", job_title: "car enthusiast", salary: "14000" },
{first_name: "Claire", job_title: "developer", salary: "15000"},
{first_name: "Clem", job_title: "developer", salary: "12000"}]
How to iterate over the above hash to output only the salary value???
I tried using:
people.each do |i,j,k|
puts "#{i}"
end
The results are as below and is not what i was intending,
{:first_name=>"Gary", :job_title=>"car enthusiast", :salary=>"14000"}
{:first_name=>"Claire", :job_title=>"developer", :salary=>"15000"}
{:first_name=>"Clem", :job_title=>"developer", :salary=>"12000"}
Is there a way to iterate through this array and simply list out only the salary values and not the rest?
In newer versions of Ruby (not sure when it was introduced, probably around ruby 2.0-ish which is when I believe keyword arguments were introduced), you can do:
people.each do |salary:,**|
puts salary
end
where ** takes all the keyword arguments that you don't name and swallows them (ie, the first_name and job_title keys in the hash). If that isn't something that your ruby version allows, you'll need to just store the entire hash in the variable:
people.each do |person|
puts person[:salary]
end
I'm very new to Ruby. I've been trying to find a way to do calculations with user inputs. There are two things that I wanted to do:
Output which friend has the most dinosaurs or jellyfish.
Output which friend has the most dinosaurs and jellyfish combined.
I only have this loop requesting for user inputs so far:
f = "yes"
while f == "yes"
print "Enter your name: "
n = gets.chomp.to_f
print "Enter the number of dinosaurs you have: "
d = gets.chomp.to_f
print "Enter the number of jellyfish you have: "
j = gets.chomp.to_f
print "Another friend? (yes/no)"
f = gets.chomp
end
Any help is appreciated. Thank you so much!
UPDATES:
Thank you so much for you all's help. I've realized that I was not specific enough. I'll try to clarify that here.
The output that I want looks something like this (things in ~ ~ are user inputs):
Enter your name: ~ Bob~
Enter the number of dinosaur you have: ~3~
Enter the number of jellyfish you have: ~6~
Another friend? (yes/no) ~yes~
Enter your name: ~ Sally~
Enter the number of dinosaur you have: ~2~
Enter the number of jellyfish you have: ~8~
Another friend? (yes/no) ~no~
Friend who has the most dinosaurs: Bob
Friend who has the most jellyfish: Sally
Friend who has the most dinosaurs and jellyfish combined: Sally
So far, the codes that I wrote only gets me to "Another friend? (yes/no)" but I'm not sure how to ask Ruby to output the last three lines that I want. Can you folks shed some light on this, please?
Thank you very much!
MORE UPDATES:
Thanks for all of your help! Got it figured out with Hash and Array. Thank you!
The answer to your question is three-fold.
Collecting user input
gets returns a line from stdin, including the newline. If the user enters Bobenter then gets returns "Bob\n".
To strip the newline you use String#chomp:
"Bob\n".chomp #=> "Bob"
To convert an input like "3\n" to an integer 3, you can use String#to_i:
"3\n".to_i #=> 3
Note that to_i ignores extraneous characters (including newline), so you can avoid chomp.
Storing data
You probably want to store a user's data in one place. In an object-oriented programming language like Ruby, such "place" is often a class. It can be as simple as:
class User
attr_accessor :name, :dinosaurs, :jellyfish
end
attr_accessor creates getters and setters, so you can read and write a user's attributes via:
user = User.new
user.name = 'Bob'
user.name #=> "Bob"
Since you don't want just one user, you need another place to store the users.
An Array would work just fine:
users = []
user = User.new
user.name = 'Bob'
user.dinosaurs = 3
users << user
users
#=> [#<User #name="Bob", #dinosaurs=3>]
Adding a second user:
user = User.new # <- same variable name, but new object
user.name = 'Sally'
user.dinosaurs = 2
users << user
users
#=> [#<User #name="Bob", #dinosaurs=3>, #<User #name="Sally", #dinosaurs=2>]
Retrieving the users (or their attributes) from the array:
users[0] #=> #<User #name="Bob">
users[0].name #=> "Bob"
users[1].name #=> "Sally"
Calculating with data
Array includes many useful methods from the Enumerable mixin.
To get an element with a maximum value there's max_by – it passes each element (i.e. user) to a block and the block has to return the value you are interested in (e.g. dinosaurs). max_by then returns the user with the highest dinosaurs value:
users.max_by { |u| u.dinosaurs }
#=> #<User #name="Bob">
You can also calculate the value:
users.max_by { |u| u.dinosaurs + u.jellyfish }
But that would currently result in an exception, because we did not set jellyfish above.
Right now you're trying to turn the name into a float (number) by calling to_f on it. It's already a string when it's coming in from the user, and should probably stay a string.
Also, you're currently overwriting each of your variables in every iteration of your loop. So if Bob fills this out, and wants to add a friend, Sally, all of Bob's information is overwritten by Sally's.
Instead, you'll need to create a Hash or Array, then add each user to it one by one from your loop.
continue = "yes"
users = {}
while continue == "yes"
print "Enter your name: "
name = gets.chomp.to_f
print "Enter the number of dinosaurs you have: "
dinosaursCount = gets.chomp.to_f
print "Enter the number of jellyfish you have: "
jellyfishCount = gets.chomp.to_f
users[name] = {dinosaurs: dinosaursCount, jellyfish: jellyfishCount}
print "Another friend? (yes/no)"
continue = gets.chomp
end
Now if Bob and Sally have both been added through the command line, you can get their data by doing:
users.Bob #{dinosaurs: 10, jellyfish: 10}
users.Bob.dinosaurs #10
users.Bob.jellyfish #10
I am trying to create users by populating the database with names in a file called names.rb which lives in the same place as seeds.rb. I am a ruby on rails / ruby newbie so please forgive me if this seems like a simple task. I keep getting the error: Cannot Load File names.rb. I am trying to do this by reading each line in the names.rb file and passing the content to the reiteration block (see below). How do I make sure that each line, for example John Kanye, Matthew Richards, etc. creates a new user with that name? Any help would be appreciated.
seeds.rb
require "names.rb"
File.open("names.rb").each { |line| puts line }
Name = line
User.create!(name: "Michael Princeton",
email: "michaelprinceton#gmail.com",
password: "foobar",
password_confirmation: "foobar",
admin: true)
5.times do |n|
name = Name
email = "example-#{n+1}#example.org"
password = "password"
User.create!(name: name,
email: email,
password: password,
password_confirmation: password)
end
names.rb
John Kanye
Matthew Richards
Mary Mary
Mike Jacobs
etc
Rename the file to names.txt, since it is not Ruby code. Then do something like this:
User.create!(
name: "Michael Princeton",
email: "michaelprinceton#gmail.com",
password: "foobar",
password_confirmation: "foobar",
admin: true
)
File.foreach('db/names.txt').with_index do |name, line_number|
User.create!(
name: name,
email: "example-#{line_number+1}#example.org",
password: 'foobar',
password_confirmation: 'foobar'
)
end
Well there's a number of problems. It's clear you don't have a firm grasp ruby yet. I highly suggest you find a tutorial or book that starts from the very beginning. That said ..
The names.rb doesn't contain ruby code, it's a text file. The file extension .txtis used for plain text, so rename the file to names.txt.
It doesn't make sense to require a text file. When you require a ruby file, it interprets and runs the code in that file. names.txt does not contain ruby code. The fix is to delete that line.
Next, it reads the lines one at a time, and prints to standard output. Then the code attempts to set the constant Name to the block variable line which is now out of scope. It proceeds to create an admin user and five other users in a loop. None of this code accomplishes your goal, so you prolly copy/pasted it from somewhere without understanding it. We've come full-circle to my suggestion to invest time in learning ruby.
What you are looking for is something like:
File.open('names.txt').each_line do |line|
User.create(name: line.strip)
end
Learning by doing is wonderful, but you should start with a solid basis.
Is there a way to covert a fixture to a set of ActionController::Parameters?
For example:
# contacts.yml
dan:
first_name: Dan
last_name: Gebhardt
email: dan#example.com
notes: Writes sample code without tests :/
joe:
first_name: Joe
last_name: Blow
email: joe#example.com
notes: Lousy plumber
# contacts_test.rb
#dan = contacts(:dan)
# create params that represent Dan?
#dan_as_params = ActionController::Parameters.new(???)
Any and all help appreciated.
You could turn the object into json and back to hash containing correct param keys thus:
h= Hash[*JSON.load(#dan.to_json).map{ |k, v| [k.to_sym, v] }.flatten]
params= {contact: h}
Update:
you can also use JSON.parse
dan= Hash[*JSON.parse(#dan.to_json, symbolize_names: true).flatten]
params= {contact: dan}
Which has its own internal way of converting json keys to symbols.
I have two complex rails (AR) queries coming from two different methods that I sometimes want to concatenate. The data structure returned in each object is the same I just want to append one onto another.
Here's a simplified example (not my actual code):
#peep1 = Person.find(1)
#peep2 = Person.find(2)
Thought something like this would work:
#peeps = #peep1 << #peep2
or this
#peeps = #peep1 + #peep2
The above is just a simplified example - joining the queries etc won't work in my case.
Edit:
Maybe concatenating is the wrong term.
Here's the output I'd like:
Say #peep1 has:
first_name: Bob
last_name: Smith
and #peep2 has:
first_name: Joe
last_name: Johnson
I want these to be combined into a third object. So if I iterate through #peeps it will contain the data from both previous objects:
#peeps has:
first_name: Bob
last_name: Smith
first_name: Joe
last_name: Johnson
Thanks!
To be frank, nothing that you are describing makes any sense :)
#peep1 and #peep2 each represent a single object -- a single row in the database.
There is no sense in which they can be meaningfully combined.
You can make an array of both of them.
#all_peeps = [#peep1, #peep2]
And then iterate over that.
#all_peeps.each do |peep|
print peep.first_name
end
This worked for me:
> #loop_feed = #job.bids.all
> #bidadd = []
> #loop_feed.each do |loop_feed|
> compare_id = loop_feed.user_id
> #user_search.each do |user|
> if compare_id == user.id
> #bidadd = [#bidadd, loop_feed].flatten
> end
> end
> end