Parsing CSV data
First, let me point out that what you are doing is not proper CSV parsing. Recommended reading: How to parse CSV data with Ruby. Carving up CSV files manually is not often ideal, as it can lead to mistakes, such as what I will explain below.
Using a well established library is most often a better choice. This also holds true of other types of data files, such as XML and JSON. Those libraries help improve consistency and reduce bugs.
Once you've chosen a library to use, read further into how to use that library effectively. You mentioned that you are pretty new, so I will leave that here as something to study when you feel ready to tackle it. =)
Bugs
Your code "works" for your simple use case, but only because you are comparing identical CSV files, rather than because the CSV files contain the same data. What your code is doing is splitting up a string (the characters from your CSV files) into lines, and comparing those. If the lines are identical, but are not in the same order, then you start getting incorrect results. For demonstration, you can try this:
sample_data1 = \
'Kanye,West
Jay,Z
Chance,TheRapper
Two,Chains'
sample_data2 = \
'Jay,Z
Kanye,West
Two,Chains
Chance,TheRapper'
# open an array for each list of names
people = Array.new
people1 = Array.new
# load each list into a variable
first_list = sample_data1
second_list = sample_data2
# rest of the code stays the same
I would expect the result to be the same either way (if it was working correctly), however this is what I get back:
Kanye West
Jay Z
Interesting. Let's see why this is happening, by adding some print
and puts
instructions in your each_line
statements. (note that I converted from curly brackets to do...end
syntax since it makes it simpler for statements which aren't one-liners)
puts '- first_list'
first_list.each_line do |line|
fields = line.split('\n')
print fields
people.push(fields)
puts
end
Did the same thing to second_list
. Here is what the console shows now. I added the comments to show you what is happening.
- first_list
["Kanye,West\n"]
["Jay,Z\n"]
["Chance,TheRapper\n"] # has newline
["Two,Chains"] # no newline
- second_list
["Jay,Z\n"]
["Kanye,West\n"]
["Two,Chains\n"] # has newline
["Chance,TheRapper"] # no newline
The issue that is causing those errors is as follows. In Ruby, single-quoted strings and double-quoted strings do not work the same way.
When you use single-quoted strings, they are string literals, so '\n'
is not the newline escape character; rather it is a backslash character and the lowercase letter n
. If you want to be able to use backslash escaping, you need to use double-quoted string "\n"
instead. (this also holds true of things like string interpolation)
You will also notice that you have an array of arrays (or 2D array), which is fine if that's what you need, but I suspect that's possibly not what you expected.
The reason each value is an array is because you are splitting them in a loop with each_line
, and then "splitting" (incorrectly due to single quote) each line again with .split('\n')
and pushing that into the array.
Implementing the above fixes:
- Splitting on
"\n"
after the file is read, not while iterating;
- Using double-quotes to look for newline characters rather than string literals of
'\n'
;
- Pushing split strings into the destination array, rather than into sub-arrays/
# open an array for each list of names
people = Array.new
people1 = Array.new
# load each list into a variable
first_list = File.open("namelist.csv", "r").split("\n")
second_list = File.open("namecomplist.csv", "r").split("\n")
# split the file up by line and push
# the contents of the line to an array
# each element is delineated by the new line
# character "\n"
first_list.each do |line|
people.push(line)
end
# split the second file up by line and push
# the contents of the line to an array
second_list.each do |line|
people1.push(line)
end
This now functions "correctly", at least to the extent you want to compare for the same lines of comma-separated characters. Note that this is still a naive solution, because if there were spaces or other unexpected characters between the commas and words, it would also not work correctly.
Review
With the nasty bug out of the way, here are some things I would do to improve your code.
First, I would suggest to make a method to read the CSV file. Flambino's answer goes into more detail on that matter, so I will just post a naive implementation that works OK. I recommend to heed Flambino's advice on this file-reading matter.
# Converts a CSV raw character string into
# an array of newline-separated strings.
def load_list_from_csv(csv_file_raw_str)
list = csv_file_raw_str.split("\n")
end
Then we can just update this:
# load each list into a variable
first_list = load_list_from_csv(sample_data1)
second_list = load_list_from_csv(sample_data2)
Or in your case...
# load each list into a variable
first_list = load_list_from_csv("namelist.csv", "r")
second_list = load_list_from_csv("namecomplist.csv", "r")
While we're at it, let's make a method to compare the 2 files. Basically just combining a few bits of code into one method. By the way, the operation you are making on those data sets is called an intersection.
Here is a working code example on repl.it showing the changes:
sample_data1 = \
'Kanye,West
Jay,Z
Chance,TheRapper
Two,Chains'
sample_data2 = \
'Jay,Z
Kanye,West
Two,Chains
Chance,TheRapper'
# Converts a CSV raw character string into
# an array of newline-separated strings.
def load_list_from_csv(csv_file_raw_str)
list = csv_file_raw_str.split("\n")
end
# Intersect two arrays to return identical strings/lines
def intersect_csv_names_lists(file1, file2)
people1 = load_list_from_csv(file1)
people2 = load_list_from_csv(file2)
[people1 & people2].join("\n")
end
# Replace commas with spaces in a string
def format_replace_commas_with_spaces(arr)
arr.gsub(',', ' ')
end
# Print the matching data and substitute a comma
# for a space for formatting purposes.
# The result should be the names of the people
# who are on both lists.
puts format_replace_commas_with_spaces(intersect_csv_names_lists(sample_data1, sample_data2))
Output:
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
Kanye West
Jay Z
Chance TheRapper
Two Chains
=> nil