In Ruby, how to read data column wise from a CSV file? - ruby-on-rails

I know how it is done row-wise
CSV.foreach(filename.csv) do |row|
puts "#{row}"
end
But I am completely lost column wise?

test.csv:
name,surname,no1,no2,no3,date
Raja,Palit,77489,24,84,12/12/2011
Mathew,bargur,77559,25,88,01/12/2011
harin,Roy,77787,24,80,12/12/2012
Soumi,paul,77251,24,88,11/11/2012
Acces by cols:
require 'csv'
csv = CSV.read('test.csv', :headers=>true)
p csv['name'] #=>["Raja", "Mathew", "harin", "Soumi"]
#or even:
t = CSV.table('test.csv')
p t[:no1] #=> [77489, 77559, 77787, 77251]
Note that in the last case the cols are accessed by their symbolized name and that strings are converted to numbers when possible.

This is the solution guys:
CSV.foreach(filename).map { |row| row[0] }
Sorry for posting it in the correct format so late.

Transpose your CSV file. By doing this your rows covert to column and viceversa. Below a simple example of transpose.
irb(main):001:0> [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]].transpose
=> [["1", "4", "7"], ["2", "5", "8"], ["3", "6", "9"]]

The method 'by_col' provides you output based on your column selection.
test_csv:
name,surname,no1,no2,no3,date
Raja,Palit,77489,24,84,12/12/2011
Mathew,bargur,77559,25,88,01/12/2011
harin,Roy,77787,24,80,12/12/2012
Soumi,paul,77251,24,88,11/11/2012
Code:
CSV.parse(File.open('test_csv'), headers: true).by_col['surname']
Output:
['Palit','bargur','Roy','paul']

Bored so decided to cook up an alternate solution here. Only works (here) if the first row has the max number of columns
columns = {}
rows.first.each_with_index do |col, i|
columns[i] = []
end
rows.each do |row|
row.each_with_index do |cell, i|
columns[i] = columns[i] + [cell]
end
end
Now you should be able to access each column by its index

Related

Rails, delete string-quotes from array, without turning it to a string

I have a method, that takes an array as argument, such as:
a = ["title", "item"]
I need to get rid of the " but I have difficulties to do so.
My goal is to achieve the following:
a = [title, item]
Two possible solutions were presented here:Getting rid of double quotes inside array without turing array into a string
eval x.to_s.gsub('"', '')
# => [1, 2, 3, :*, :+, 4, 5, :-, :/]
and
x=["1", "2", "3", ":*", ":+", "4", "5", ":-", ":/"]
=> ["1", "2", "3", ":*", ":+", "4", "5", ":-", ":/"]
x.map{|n|eval n}
=> [1, 2, 3, :*, :+, 4, 5, :-, :/]
I tried both of these solutions, but It always leads to this error:
undefined local variable or method `title'
How do I get rid off these " quotes in an array?
Edit:
I need to alter an array. This is what I am trying to do:
a = ["title", "item"]
should change to something like:
a = [model_class.human_attribute_name(:title), model_class.human_attribute_name(:title)]
(It's about translations).
This code is in a model.rb, maybe that helps.Here is my full code:
def humanifier(to_translate_array)
translated = []
to_translate_array.each do |element|
translated.push("model_class.human_attribute_name(:#{element})")
end
return translated
end
It looks like you want to translate strings into symbols, you can do that with #to_sym
to_translate_array.each do |element|
translated.push("model_class.human_attribute_name(#{element.to_sym})")
end
Or if you actually want the translated value, (and not just a string "model_class.human...")
to_translate_array.each do |element|
translated.push(model_class.human_attribute_name(element.to_sym))
end
"title" is a string, :title is a symbol.

match key of hash and then fetch values accordingly in ruby

I have included the given code:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = params[:test] #I get "1,3,7"
I get this, now please guide me how to match keys with #amaze and accordingly fetch its values from #classes i.e USA, France, Delhi.
Since #amaze is just a String, lets first convert it in Array so its easy to enumerate:
#amaze = "1,3,7"
#amaze = #amaze.split(",")
# => ["1", "3", "7"]
Now, since you have all keys extract all values:
#amaze.map { |i| #classes[i.to_i] }
# => ["USA", "France", "Delhi"]
Split #amaze by , and get an array of keys, convert them into Integer, then select only those key/value pairs which key is into this array of keys. Something like this:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = "1,3,7" #I get "1,3,7"
arr = #amaze.split(',').map(&:to_i)
p #classes.select{|el| arr.include? el}
Result:
#> {1=>"USA", 3=>"France", 7=>"Delhi"}
If you want values only use .values:
p #classes.select{|el| arr.include? el}.values
Result:
#> ["USA", "France", "Delhi"]
For what(seemingly) you are asking, the below line will do it:
#amaze.split(",").each { |i| p #classes[i.to_i] }
# If #amaza = "1,3,7", above line will output:
# => "USA"
# "France"
# "UK"
This should work well for you:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = params[:test].split(",").map(&:to_i)
#classes.values_at(*#amaze)
#=> ["USA", "France", "Delhi"]
Hash#values_at accepts an indefinite number of keys and returns their values as an array. The * (splat) operator explodes the array so this call actually becomes #classes.values_at(1,3,7) Docs
Might also want to add a compact to the end in the event a key does not exist. e.g
#amaze = params[:test].split(",").map(&:to_i) # Asssume this returns [1,3,7,9]
#classes.values_at(*#amaze)
#=> ["USA", "France", "Delhi",nil]
#classes.values_at(*#amaze).compact
#=> ["USA", "France", "Delhi"]
I think a clearer understanding of hashes would help you out here.
A Hash is a data structure that is a list of key-value pairs. For example, the following is a Hash object of key-value pairs (your example):
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
If you want to extract a value from #classes, you need to pass the key of the value you want. If we wanted "USA" we would pass the key of 1 to #classes. If we wanted "France", we would pass it the key of 3:
#classes[1] would return "USA" and #classes[3] would return "France".
It's not clear what data structure #amaze is according to your question, but let's say it's the string "1, 3, 7" which we can split to create an array [1, 3, 7].
You could iterate over the array to get each of the values from #classes:
#amaze.split(",").map(&:to_i).each do |key|
puts #classes[key]
end
That would print out each of the corresponding values to keys in #classes.

Create array of arrays from database query in rails

Ive got a table which consists of two fields called follower_id and followed_id. I need to create a query which creates an array of from each row and puts that in an overall array so that the end structure looks like:
"edges": [
["1", "2"],
["1", "3"],
["3", "4"],
["3", "5"]
]
so far I have
def self.including_relationships
result={}
result["edges"] Relationship.all.each do |relationship|
result[""]= Relationship.select(:follower_id.to_s,:follower_id.to_s)
#the code here is called once for each user
# user is accessible by 'user' variable
end
result
end
but this produces:
edges: [
"[4, 3, 3, 4]",
"[3, 4, 3, 4]"
]
You can use map to build an array like:
Relationship.all.map { |r| [r.follower_id.to_s, r.followed_id.to_s] }
Try this
relationships = Relationship.all.map { |r| [r.follower_id.to_s, followed_id.to_s] }
results = {"edges": relationships }

Remove duplicates from an array of array

How do I remove duplicates from this array?
product_areas = [["1", "2", "3"], ["3", "1", "2"]]
I have tried product_areas.uniq!, product_area.uniq but the same thing is repeating. What am I missing here?
Expected Output:
product_areas = ["1", "2", "3"]
Try this:
product_areas = [["1", "2", "3"], ["3", "1", "2"]].flatten.uniq
Using flatten on your array will create the following result:
["1", "2", "3", "3", "1", "2"]
When you call uniq on that array, you will get the result you were expecting:
["1", "2", "3"]
As previously pointed out
product_areas = [["1", "2", "3"], ["3", "1", "2"]].flatten.uniq
-OR-
product_areas.flatten.uniq!
will both lead you to your desired answer.
Why?
When you were running "product_areas.uniq!" the process was comparing the two inner arrays against each other, other than the elements of each array. Because both ["1", "2", "3"] and ["3", "1", "2"] are unique in the array, neither will be removed. As an example say you had the following array
product_areas = [["1", "2", "3"], ["3", "1", "2"], ["1","2","3"]]
and you ran:
product_areas = product_areas.uniq
product_areas would then look like the following:
product_areas = [["1", "2", "3"], ["3", "1", "2"]]
What you need to be aware of when running any sort of enumerable method on arrays is it will only move down to each individual element. So if inside an array you have more arrays, any iterative method will look at the inner array as a whole. Some sample code to demonstrate this:
array_of_arrays = [[1,2,3], [4,5,6]]
array_of_arrays.each do |array|
p array
end
#---OUPUT---
# [1, 2, 3]
# [4, 5, 6]
array_of_arrays.each do |array|
array.each do |element|
p element
end
end
#---OUPUT---
# 1
# 2
# 3
# 4
# 5
# 6
I've used this little snippet of code over and over again in my career as a Ruby on Rails developer to solve this frequently encountered problem in a single piece of neat little code. The end result of this code is that you can just call something like
product_areas.squish
to do the following:
flatten 2-D arrays into 1-D arrays
remove nil elements
remove duplicate elements
I do this by adding an initializer config/initializer/core_ext.rb to my project which extends the core Ruby functionality as follows:
class Array
def squish
blank? ? [] : flatten.compact.uniq
end
end
class NilClass
def squish
[]
end
end

Get & store digits from string

I have a string of values like this:
=> "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
I want to put each value in an array so I can use each do. So something like this:
#numbers =>["72", "58", "49", "62", "9", "13", "17", "63"]
This is the code I want to use once the string is a usable array:
#numbers.each do |n|
#answers << Answer.find(n)
end
I have tried using split() but the characters are not balanced on each side of the number. I also was trying to use a regex split(/\D/) but I think I am just getting worse ideas.
The controller:
#scores = []
#each_answer = []
#score.answer_ids.split('/').each do |a|
#each_answer << Answer.find(a).id
end
Where #score.answer_ids is:
=> "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
Looks like an array of JSON strings. You could probably use Ruby's built-in JSON library to parse it, then map the elements of the array to integers:
input = "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
require 'json'
ids = JSON.parse(input).map(&:to_i)
#answers += Answer.find(ids)
I'd use:
foo = "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
foo.scan(/\d+/) # => ["3", "4", "60", "71", "49", "62", "9", "14", "17", "63"]
If you want integers instead of strings:
foo.scan(/\d+/).map(&:to_i) # => [3, 4, 60, 71, 49, 62, 9, 14, 17, 63]
If the data originates inside your system, and isn't the result of user input from the wilds of the Internet, then you can do something simple like:
bar = eval(foo) # => ["3", "4", "60", "71", "49", "62", "9", "14", "17", "63"]
which will execute the contents of the string as if it was Ruby code. You do NOT want to do that if the input came from user input that you haven't scrubbed.
In your code n is a String, not an Integer. The #find method expects an Integer, so you need to convert the String to an Array of Integers before iterating over it. For example:
str = "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
str.scan(/\d+/).map(&:to_i).each do |n|
#answers << Answer.find(n)
end

Resources