Arrays have always been my downfall in every language I've worked with, but I'm in a situation where I really need to create a dynamic array of multiple items in Rails (note - none of these are related to a model).
Briefly, each element of the array should hold 3 values - a word, it's language, and a translation into English. For example, here's what I'd like to do:
myArray = Array.new
And then I'd like to push some values to the array (note - the actual content is taken from elsewhere - although not a model - and will need to be added via a loop, rather than hard coded as it is here):
myArray[0] = [["bonjour"], ["French"], ["hello"]]
myArray[1] = [["goddag"], ["Danish"], ["good day"]]
myArray[2] = [["Stuhl"], ["German"], ["chair"]]
I would like to create a loop to list each of the items on a single line, something like this:
<ul>
<li>bonjour is French for hello</li>
<li>goddag is Danish for good day</li>
<li>Stuhl is German for chair</li>
</ul>
However, I'm struggling to (a) work out how to push multiple values to a single array element and (b) how I would loop through and display the results.
Unfortunately, I'm not getting very far at all. I can't seem to work out how to push multiple values to a single array element (what normally happens is that the [] brackets get included in the output, which I obviously don't want - so it's possibly a notation error).
Should I be using a hash instead?
At the moment, I have three separate arrays, which is what I've always done, but I don't particularly like - that is, one array to hold the original word, one array to hold the language, and a final array to hold the translation. While it works, I'm sure this is a better approach - if I could work it out!
Thanks!
Ok, let's say you have the words you'd like in a CSV file:
# words.csv
bonjour,French,hello
goddag,Danish,good day
stuhl,German,chair
Now in our program we can do the following:
words = []
File.open('words.csv').each do |line|
# chomp removes the newline at the end of the line
# split(',') will split the line on commas and return an array of the values
# We then push the array of values onto our words array
words.push(line.chomp.split(','))
end
After this code is executed, the words array had three items in it, each item is an array that is based off of our file.
words[0] # => ["bonjour", "French", "hello"]
words[1] # => ["goddag", "Danish", "good day"]
words[2] # => ["stuhl", "German", "chair"]
Now we want to display these items.
puts "<ul>"
words.each do |word|
# word is an array, word[0], word[1] and word[2] are available
puts "<li>#{word[0]} is #{word[1]} for #{word[2]}</li>"
end
puts "</ul>"
This gives the following output:
<ul>
<li>bonjour is French for hello</li>
<li>goddag is Danish for good day</li>
<li>stuhl is German for chair</li>
</ul>
Also, you didn't ask about it, but you can access part of a given array by using the following:
words[0][1] # => "French"
This is telling ruby that you want to look at the first (Ruby arrays are zero based) element of the words array. Ruby finds that element (["bonjour", "French", "hello"]) and sees that it's also an array. You then asked for the second item ([1]) of that array and Ruby returns the string "French".
You mean something like this?
myArray.map{|s|"<li>#{[s[0],'is',s[1],'for',s[2]].join(" ")}</li>"}
Thanks for your help guys! I managed to figure a solution out based on your advice
For the benefit of anyone else who stumbles across this problem, here's my elided code. NB: I use three variables called text, language and translation, but I suppose you could replace these with a single array with three separate elements, as Jason suggests above.
In the Controller (content is being added via a loop):
#loop start
my_array.push(["#{text}", "#{language}", "#{translation}"])
#loop end
In the View:
<ul>
<% my_array.each do |item| %>
<li><%= item[0] # 0 is the original text %> is
<%= item[1] # 1 is the language %> for
<%= item[2] # 2 is the translation %></li>
<% end %>
</ul>
Thanks again!
Related
I'm going to preface that I'm still learning ruby.
I'm writing a script to parse a .csv and identify possible duplicate records in the data-set.
I have a .csv file with headers, so I'm parsing the data so that I can access each row using a header title as such:
#contact_table = CSV.parse(File.read("app/data/file.csv"), headers: true)
# Prints all last names in table
puts contact_table['last_name']
I'm trying to iterate over each row in the table and identify if the last name I'm currently iterating over is similar to the next last name, but I'm having trouble doing this. I guess the way I'm handling it is as if it's an array, but I checked the type and it's a CSV::Row.
example (this doesn't work):
#contact_table.each_with_index do |c, i|
puts "first contact is #{c['last_name']}, second contact is #{c[i + 1]['last_name']}"
end
I realized this doesn't work like this because the table isn't an array, it's a CSV::Row like I previously mentioned. Is there any method that can achieve this? I'm really blanking right now.
My csv looks something like this:
id,first_name,last_name,company,email,address1,address2,zip,city,state_long,state,phone
1,Donalt,Canter,Gottlieb Group,dcanter0#nydailynews.com,9 Homewood Alley,,50335,Des Moines,Iowa,IA,515-601-4495
2,Daphene,McArthur,"West, Schimmel and Rath",dmcarthur1#twitter.com,43 Grover Parkway,,30311,Atlanta,Georgia,GA,770-271-7837
#contact_table should be a CSV::Table which is a collection of CSV::Rows so in this:
#contact_table.each_with_index do |c, i|
...
end
c is a CSV::Row. That's why c['last_name'] works. The problem is that here:
c[i + 1]['last_name']
you're looking at c (a single row) instead of #contact_table, if you said:
#contact_table[i + 1]['last_name']
then you'd get the next last name or, when c is the last row, an exception because #contact_table[i+1] will be nil.
Also, inside the iteration, c is the current (or (i+1)th) row and won't always be the first.
What is your use case for this? Seems like a school project?
I recommend for_each instead of parse (see this comparison). I would probably use a Set for this.
Create a Set outside of the scope of parsing the file (i.e., above the parsing code). Let's call it rows.
Call rows.include?(row) during each iteration while parsing the file
If true, then you know you have a duplicate
If false, then call rows.add(row) to add the new row to the set
You could also just fill your set with an individual value from a column that must be distinct (e.g., row.field(:some_column_name)), such as email or phone number, and do the same inclusion check for that.
(If this is for a real app, please don't do this. Use model validations instead.)
I would use #read instead of #parse and do something like this:
require 'csv'
LASTNAME_INDEX = 2
data = CSV.read('data.csv')
data[1..-1].each_with_index do |row, index|
puts "Contact number #{index + 1} has the following last name : #{row[LASTNAME_INDEX]}"
end
#~> Contact number 1 has the following last name : Canter
#~> Contact number 2 has the following last name : McArthur
I have looked around for info on how to do this, just can't quite get it myself. Fairly new to Ruby and I'm building a gem. I can return a list of results from a website ok with Nokogiri.
The issue I'm having is how to take an input number and relate it to another piece of text from the website I'm scraping. So you pick a movie title by number from a list and then you return the relevant movie outline. Maybe I don't want to iterate through all the objects (again) at all.
This is what I'm looking at:
def menu
input = nil
while input != "exit"
input = gets.strip.downcase
if input.to_i < 24
#movies.each.with_index(1) do |movie, i|
puts "Description: #{movie.outline}"
end
end
end
end
So we create a variable for input. While the input is not the word exit and less than the number 24, we iterate through the movies and put the relevant one by index number. Currently putting ALL the movie outlines so I feel like I should ditch the iteration. I've tried a number of things around adding the input to movie.outline...
Any help or hints would be great!
It's hard to say for sure, but, are you maybe just trying to do this?
input = gets.to_i
if input < 24
movie = #movies[input]
puts "Description: #{movie.outline}"
end
Or something to that effect?
If you want to access an Array element, you use the [] notation (I'm assuming #movies is an Array, but if it's some other enumerable, you'll need to tell us what that is).
Suppose I have an array of string like following:
str_arr=["10$", "10$ I am a student","10$Good","10$ Nice weekend!"]
I would like to re-organize the array element value in the way that, in each element of the array , if there is(are) white space(s) after 10$ sign, then, combine the 10$ with the following word.
That's generating a new array like following:
str_arr=["10$", "10$I am a student","10$Good","10$Nice weekend!"]
What I tried to do is like following:
str_arra.map{|elem|
# not sure how to do here,
#split and check then combine again?
if elem.size>1
words=elem.split()
if words[0]=='10$'
#not sure how to do here
end
elsif elem.size==1
elem
end
}
but not sure how to generate the new array ... and the code above seems verbose...
P.S. it is possible that there are multiple white-spaces after 10$, then comes a word
If you only have those cases, the following should do the trick:
["$ abc", "$str"].map {|v, k| v.sub(/\$ +/, '$')}
Here's an example: http://codepad.org/XHeo7E8B
Do this:
str_arra.map{|elem| elem.gsub(/^\$ /, "$") }
A project can have many tags. When editing a project I'd like to list all tags in a input field (stackoverflow style). In Rails 3 I have the following code, where I push all my names into an array before calling join(' ') but is there a quicker / more elegant way?
#tags = #project.tags
#tags_array = []
#tags.each do |tag|
#tags_array << tag.name
end
#tags_string = #tags_array.join(' ')
Maybe what you want is the Enumerable#collect method:
#tags_string = #project.tags.collect(&:name).join(' ')
Collect comes in handy when you're trying to transform one list into another list of equal size, which is exactly the pattern here.
The &:name part means "call method name on the given object" and is something that could be spelled out as { |t| t.name } equivalently.
The Enumerable library is really great and you should have a look through it and be familiar with the various methods as it can save you a ton of time.
One way is to use threads while iterating through the #tags array. Have a look at this excellent article http://rubysource.com/threading-ruby/
Secondly it seems #tag is an array
I would use map to return an array of names and then join them to a string
names = #tags.map{|tag| tag.name}.join(' ')
No matter what language I'm using I always need to display a list of strings separated by some delimiter.
Let's say, I have a collection of products and need to display its names separated by ', '.
So I have a collection of Products, where each one has a 'name' attribute. I'm looking for some Rails method/helper (if it doesn't exist, maybe you can give me ideas to build it in a rails way) that will receive a collection, an attribute/method that will be called on each collection item and a string for the separator.
But I want something that does not include the separator at the end, because I will end with "Notebook, Computer, Keyboard, Mouse, " that 2 last characters should not be there.
Ex:
concat_ws(#products, :title, ", ")
#displays: Notebook, Computer, Keyboard, Mouse
Supposing #products has 4 products with that names of course.
Thanks!
you should try the helper to_sentence.
If you have an array, you can do something like
array.to_sentence. If your array has the data banana, apple, chocolate it will become:
banana, apple and chocolate.
So now if you have your AR Model with a field named, you could do something like
MyModel.all.map { |r| r.name }.to_sentence
#products.map(&:title).join(', ')
As #VP mentioned, Array#to_sentence does this job well in rails. The code for it is here:
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/array/conversions.rb
Saying that, its use of the Oxford Comma is questionable :-)