I'm getting from csv file some data (also how to select first 20 in csv?), for example:
A B C
D E F
also method:
def common_uploader
require 'csv'
arr = CSV.read("/#{Rails.public_path}/uploads_prices/"+params[:file], {:encoding => "CP1251:UTF-8", :col_sep => ";", :row_sep => :auto, :headers => :none})
#csv = []
#csv << arr
end
so it is array of arrays...
But how can i view it normaly in haml view?
How can i view array of arrays?
i tried something like (also in each file i have different count of columns):
view:
= #csv.first.each do |a|
= a[:1]
Help me please to view csv data.
There are several ways you can read a set number of records, and you need to pick which to use based on the anticipated size of your data source.
Starting with a CSV file:
A1,A2,A3
B1,B2,B3
C1,C2,C3
D1,D2,D3
E1,E2,E3
F1,F2,F3
The simplest ways to read a fixed number of records would be one of these:
require 'csv'
array = CSV.read('test.csv')[0, 2]
Which returns an array of two sub-arrays:
[
[0] [
[0] "A1",
[1] "A2",
[2] "A3"
],
[1] [
[0] "B1",
[1] "B2",
[2] "B3"
]
]
An alternate is:
File.new('test.csv').readlines[0, 2].map{ |l| CSV.parse(l).flatten }
Which returns the same result, an array of two sub-arrays:
[
[0] [
[0] "A1",
[1] "A2",
[2] "A3"
],
[1] [
[0] "B1",
[1] "B2",
[2] "B3"
]
]
Both of these are fine for small input files, but will have problems if you are reading a few lines from a big input file. They will force Ruby to read the entire file into memory and create an intermediate array before slicing off the number of records you want. Where I work it's nothing for us to get gigabyte file sizes, so grabbing a small section of those files would make Ruby and the system do an inordinate amount of work building the intermediate array then throwing it away.
You are better off to only read the minimum number of records needed. Sometimes lines need to be skipped before reading; This demonstrates that idea, along with handling EOFError if the input file's EOF is encountered unexpectedly:
File.open('test.csv') do |fi|
array = []
begin
5.times { fi.readline }
2.times.each{ array += CSV.parse(fi.readline) }
rescue EOFError
end
end
Replace 5 with the number of records to skip, and 2 with the number to read. For that example I deliberately read off the end of the file to show how to skip lines, read some and then handle the EOF situation cleanly.
The data looks like:
[
[0] [
[0] "F1",
[1] "F2",
[2] "F3"
]
]
Because I'm using File.open with a block, the file is closed automatically after the block exists, avoiding leaving an open filehandle hanging around.
The HAML output section of your question isn't well defined at all, but this is one way to output the data:
array = []
File.open('test.csv') do |fi|
begin
0.times { fi.readline }
2.times.each{ array += CSV.parse(fi.readline) }
rescue EOFError
end
end
require 'haml'
engine = Haml::Engine.new(<<EOT)
%html
%body
%table
- array.each do |r|
%tr
- r.each do |c|
%td= c
EOT
puts engine.render(Object.new, :array => array)
Which results in this output of a simple HTML table:
<html>
<body>
<table>
<tr>
<td>A1</td>
<td>A2</td>
<td>A3</td>
</tr>
<tr>
<td>B1</td>
<td>B2</td>
<td>B3</td>
</tr>
</table>
</body>
</html>
EDIT:
and my test file: dl.dropbox.com/u/59666091/qnt_small.csv i want to see this 7 columns in browser (via haml view)
Using this as my test data:
a1,a2,a3,a4,a5,a6,a7
b1,b2,b3,b4,b5,b6,b7
c1,c2,c3,c4,c5,c6,c7
d1,d2,d3,d4,d5,d6,d7
e1,e2,e3,e4,e5,e6,e7
and this code:
require 'csv'
array = []
File.open('test.csv') do |fi|
begin
0.times { fi.readline }
2.times.each{ array += CSV.parse(fi.readline) }
rescue EOFError
end
end
require 'haml'
engine = Haml::Engine.new(<<EOT)
%html
%body
%table
- array.each do |r|
%tr
- r.each do |c|
%td= c
EOT
puts engine.render(Object.new, :array => array)
I get this output:
<html>
<body>
<table>
<tr>
<td>a1</td>
<td>a2</td>
<td>a3</td>
<td>a4</td>
<td>a5</td>
<td>a6</td>
<td>a7</td>
</tr>
<tr>
<td>b1</td>
<td>b2</td>
<td>b3</td>
<td>b4</td>
<td>b5</td>
<td>b6</td>
<td>b7</td>
</tr>
</table>
</body>
</html>
I made a minor change to move array outside the File.open block, nothing else is different.
You can do sth like that:
arr = CSV.read("/data.csv", {:encoding => "CP1251:UTF-8", :col_sep => ",", :row_sep => :auto, :headers => :none})
i = 0
arr.each do |row|
if i == 20
break
else
i += 1
end
# do sth
puts row
end
But I think it's not a beautiful solution.
Edit:
I don't understand why you put a instance of CSV into array, you wouldn't get a each row of csv file into array
require 'csv'
def parser
CSV.open('data.csv', 'r', ';') do |row|
puts row
end
Related
I am moving some of my scraping from JavaScript to Ruby, and I am having trouble using Nokogiri.
I have trouble getting the right <dl> in a target class. I tried using css and xpath with the same result.
This is a sample of the HTML:
<div class="target">
<dl>
<dt>A:</dt>
<dd>foo</dd>
<dt>B:</dt>
<dd>bar</dd>
</dl>
</div>
This is a sample of my code:
require 'rubygems'
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::HTML(open(url))
doc.css(".target > dl").each do |item|
puts item.text # I would expect to receive a collection of nodes here,
# yet I am receiving a single block of text
end
doc.css(".target > dl > dt").each do |item|
puts item.text # Here I would expect to iterate through a collection of
# dt elements, however I receive a single block of text
end
Can someone show me what I am doing wrong?
In the first case, the result should be the single dl; you are getting a single block of text. That is expected.
In the second case, the result should be two individual dt elements. You are printing their text one after another, which is indistinguishable from printing the text of the dl.
doc.css('.target > dl').length
# => 1 # as you have one `dl` element in `.target`
doc.css('.target > dl > dt').length
# => 2 # as you have two `dt` elements that are children of a `dl` in `.target`
doc.css(".target > dl > dt").each do |item|
puts item.text
puts "---" # make it obvious which element is which
end
# => A:
# ---
# B:
# ---
I am not quite sure what other result you are expecting.
I'd use something like:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<div class="target">
<dl>
<dt>A:</dt>
<dd>foo</dd>
<dt>B:</dt>
<dd>bar</dd>
</dl>
</div>
EOT
This finds the first class='target', then its contained <dt> tags, and extracts each <dt>'s text:
doc.at('.target').search('dt').map{ |n| n.text } # => ["A:", "B:"]
This does the same only passing the text to map as shorthand:
doc.at('.target').search('dt').map(&:text) # => ["A:", "B:"]
This lets the engine find all <dt> embedded in all class="target" tags:
doc.search('.target dt').map(&:text) # => ["A:", "B:"]
See "How to avoid joining all text from Nodes when scraping" also.
I have narrowed down a 33,364 entry XML file to the 1,068 that I need. Now I am attempting to gather pieces of information from each node that I have narrowed my search down to, and store each piece of information in a hash, so that I can list out the relevant data in a rails view.
Here is the code in my controller (home_controller.rb) --
class HomeController < ApplicationController
# REQUIRE LIBRARIES
require 'nokogiri'
require 'open-uri'
def search
end
def listing
#properties = {}
# OPEN THE XML FILE
mits_feed = File.open("app/assets/xml/mits.xml")
# OUTPUT THE XML DOCUMENT
doc = Nokogiri::XML(mits_feed)
doc.xpath("//Property/PropertyID/Identification[#OrganizationName='northsteppe']").each do |property|
# GATHER PROPERTY INFORMATION
information = {
"street_address" => property.xpath("Address/AddressLine1").text,
"city" => property.xpath("Address/City").text,
"zipcode" => property.xpath("Address/PostalCode").text,
"short_description" => property.xpath("Information/ShortDescription").text,
"long_description" => property.xpath("Information/LongDescription").text,
"rent" => property.xpath("Information/Rents/StandardRent").text,
"application_fee" => property.xpath("Fee/ApplicationFee").text,
"bedrooms" => property.xpath("Floorplan/Room[#RoomType='Bedroom']/Count").text,
"bathrooms" => property.xpath("Floorplan/Room[#RoomType='Bathroom']/Count").text,
"bathrooms" => property.xpath("ILS_Unit/Availability/VacancyClass").text
}
# MERGE NEW PROPERTY INFORMATION TO THE EXISTING HASH
#properties.merge(information)
end
end
end
I'm not getting any errors and my view is loading fine, but it is pulling up blank. Here is my view file (listing.html.erb) --
<div class="propertiesHolder">
<% if #properties %>
<ul>
<% #properties.each do |property| %>
<li><%= property.information.street_address %></li>
<% end %>
</ul>
<% else %>
<h1>There are no properties that match your search</h1>
<% end %>
</div>
Does anyone know why this might be pulling up blank? I would assume that I would receive an error if I had done something incorrect in the code. I also tried just outputting "Hello World" as text for each |property| and this also pulled up blank. Thank you!
Ruby merge does not mutate your hash. It just returns the two hashes as one.
Example
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge(h2)
#=> {"a"=>100, "b"=>254, "c"=>300}
h1
#=> {"a"=>100, "b"=>200}
Note how h1 still retains its original values?
What you will want to do is rename your information hash to #properties. I suggest this because you are merging a hash with information in it (information) with an empty hash (#properties). So instead of overwriting when you merge the hashes, just use the first hash.
I am new to Rails and I am using Ruby version 1.9.3 and Rails version 3.0.0.
I want to print an array in Rails. How do I do that?
For example, we have to use print_r to print an array in PHP:
<?php
$a = array ('a' => 'apple', 'b' => 'banana', 'c' => array ('x', 'y', 'z'));
print_r ($a);
?>
Output:
<pre>
Array
(
[a] => apple
[b] => banana
[c] => Array
(
[0] => x
[1] => y
[2] => z
)
)
</pre>
How do I print an array in Rails?
You can use inspect like:
#a = ['a', 'b']
p #a #['a', 'b']
Or:
p #a.inspect #"[\"a\", \"b\"]"
You need to use awesome_print gem.
require 'awesome_print'
hash = {:a=>1,:b=>2,:c => [1,2,3]}
ap hash
output:
{
:a => 1,
:b => 2,
:c => [
[0] 1,
[1] 2,
[2] 3
]
}
It depends on what you want to use the array for.
To blindly output an array in a view, which has to be in a view, you should use debug and inspect like this:
<%= #array.inspect() %>
<%= debug #array %>
However, if you want to iterate through an array, or do things like explode(), you'll be better suited using the Ruby array functions.
You've got a couple of options here. I'm assuming you're doing this in an ERB template.
This will convert the array to YAML and print it out surrounded in <pre> tags
<%= debug [1,2,3,4] %>
And this will print it out formatted in a readable Ruby syntax:
<pre><%= [1,2,3,4].inspect %></pre>
Check out "Debugging Rails Applications" for more info.
I have the following array of hashes and I need to parse all email that are not empty:
[{:id=>"something", :first_name=>"First", :last_name=>"Name", :name=>"First Name", :email=>"first_name#gmail.com", :gender=>nil, :birthday=>nil, :profile_picture=>nil, :relation=>nil},
{...},
{}]
I am trying to do that this way:
- #contacts[0].each_with_index do |c, i|
- unless c[:email].blank?
%tr
%td= c[:email]
%td= check_box_tag "email_to[]", c[:email], true
But I am getting error:
An ActionView::Template::Error occurred in users#parse_data:
no implicit conversion of Symbol into Integer
How to do it right?
It's not good design to do a lot of processing in your views. It's understandable to have simple conditionals and loops, but heavy processing should occur in the controller.
Use something along these lines:
ary = [
{:id=>"something", :first_name=>"First", :last_name=>"Name", :name=>"First Name", :email=>"first_name#gmail.com", :gender=>nil, :birthday=>nil, :profile_picture=>nil, :relation=>nil},
{:id=>"something", :first_name=>"First", :last_name=>"Name", :name=>"First Name", :email=>"", :gender=>nil, :birthday=>nil, :profile_picture=>nil, :relation=>nil},
{}
]
viewable_email = ary.reject{ |e| e.empty? || e['email'].empty? }
At this point viewable_email would contain only the hashes to be displayed. Your view would only loop over them.
#contacts.each_with_index do |c, i| ...
When you say each_with_index on a hash you will get an array like below
{:id=>"something", :first_name=>"First", :last_name=>"Name"}.each_with_index{|e,i| p e}
[:id, "something"]
[:first_name, "First"]
[:last_name, "Name"]
So, you cant say e[:id], thats why the error. As mentioned above #contacts[0] will give you the hash and not an array.
I have the following code in my rails controller:
#main_mastertest = $connection.execute("SELECT * FROM builds;")
#l2_tmp_mastertest = Array.new
#l2_tmp_mastertest = #main_mastertest.map {|y|[y[0]]}
#l2_mastertest = Array.new
#l2_mastertest = #l2_tmp_mastertest.inject(Hash.new(0)) { |hash,element|
hash[element] +=1
hash }
#l2_mastertest = #l2_mastertest.sort_by { |x, _| x }.reverse
After that I try to do something like this in my view :
<% #l2_mastertest.each_with_index do |row1, index1| %>
<% #l2_mastertest.each_with_index do |row2, index2| %>
<% if row2[0][ /(\d+\.\d+)/ ].to_s == row1[0].to_s %> # LINE X
..................
<% end %>
<% end %>
<% end %>
But it gives me an error on line X saying : can't convert Regexp into Integer
If I simulate what I think is going on with the data structures in your question, I get this:
#l2_mastertest = { '3.1.4' => 7, '1.2.3' => 8 }
=> {"3.1.4"=>7, "1.2.3"=>8}
#l2_mastertest.each_with_index { |row2,index| p row2,index }
["3.1.4", 7]
0
["1.2.3", 8]
1
So a structure like row2[0][ /(\d+\.\d+)/ ] is the same as e.g. "3.1.4"[ /(\d+\.\d+)/ ]
Edit: Removed my previous "answer", because actually "3.1.4"[ /(\d+\.\d+)/ ] => "3.1", which is fine in Ruby (and I learned something today :-). Something else (possibly in code not shown) is making the #l2_mastertest hash not behave as expected.
Potentially this is a database/model issue, and as commenters have suggested, row[0] does not contain a string.
I would suggest instead that SELECT * FROM builds; is risky, since you are relying on database to return columns in particular order. You should change it to fetch the column data you need, in order e.g.
SELECT version, result, build_id FROM builds;
so that you are certain that later processing is working with the column that you think it is. Simply re-building or transferring the database could change the order that the RDBMS returns the columns in a SELECT * and make previously-working code break.