I'm trying to use Array.select to separate out, and then delete, strings from a database that contain unwanted items. I get no errors but this does not seem to be working as hoped.
The relevant code is the last part:
totaltext = []
masterfacs = ''
nilfacs = ''
roomfacs_hash = {'lcd' => lcd2, 'wifi'=> wifi2, 'wired' => wired2, 'ac' => ac2}
roomfacs_hash.each do |fac, fac_array|
if roomfacs.include? (fac)
totaltext = (totaltext + fac_array)
masterfacs = (masterfacs + fac + ' ')
else
nilfacs = (nilfacs + fac + ' ')
end
end
finaltext = Array.new
text_to_delete = totaltext2.select {|sentences| sentences =~ /#{nilfacs}/i}
finaltext = totaltext2.delete (text_to_delete)
puts finaltext
It's probably not working because delete isn't a chainable method (the return value is the object you are trying to delete on success, or nil if not found; not the modified array). To simplify your code, just use reject
finaltext = totaltext.reject{|sentence| nilfacs.any?{|fac| sentence =~ /#{fac}/i } }
Related
I am using ruby to merge CSV files that might contain different headers.
my problem is that some of the values in the CSV files are quite complicated and when data get lost in the merge process
for example the original value: "[cell([""A"",""B""]),""X""+cell([""A"",""C""])+""W""].join(""_"")" will be written as "[cell([""A"",v1,""B""]),
and as a result I get CSV::MalformedCSVError (CSV::MalformedCSVError) when trying to read the merged file.
how can I read and write the exact content of each CSV cell?
my code and running example:
def join_multiple_csv(csv_path_array)
f = CSV.parse(File.read(csv_path_array[0]), :headers => true, :quote_char => "'")
f_h = {}
f.headers.each {|header| f_h[header] = f[header]}
n_rows = f.size
csv_path_array.shift(1)
csv_path_array.each do |csv_file|
curr_csv = CSV.parse(File.read(csv_file), :headers => true, :quote_char => "'")
curr_h = {}
curr_csv.headers.each {|header| curr_h[header] = curr_csv[header]}
new_headers = curr_csv.headers - f_h.keys
exist_headers = curr_csv.headers - new_headers
new_headers.each { |new_header|
f_h[new_header] = Array.new(n_rows) + curr_csv[new_header]
}
exist_headers.each {|exist_header|
f_h[exist_header] = f_h[exist_header] + curr_csv[exist_header]
}
n_rows = n_rows + curr_csv.size
end
csv_headers = f_h.keys.map {|string| string}
output = csv_headers.join(",") + "\n"
(0..n_rows-1).each do |i|
row = ''
f_h.each_key do |header|
if f_h[header][i].nil?
row.concat(f_h[header][i].to_s + ",")
else
row.concat(f_h[header][i].to_s + ",")
end
end
output.concat(row + "\n")
end
return output
end
csv_files = ['f1.csv', 'f2.csv']
outputs = join_multiple_csv(csv_files)
f = CSV.new(outputs)
row = f.readline
while row do
row = f.readline
end
running example:
f1.csv
H1,H3,H4
v1,v2,v3
f2.csv
H2,H3,H4
v1,v3,"[cell([""A"",""B""]),""X""+cell([""A"",""C""])+""W""].join(""_"")"
expected output:
H1,H2,H3,H4
v1,,v2,v3
,v1,v3,"[cell([""A"",""B""]),""X""+cell([""A"",""C""])+""W""].join(""_"")"
output:
H1,H3,H4,H2,
v1,v2,v3,,,
,v3,"[cell([""A"",v1,""B""]),
,,,,,
,,,,,
Any idea what can I do?
Sorry I answered in rush.
I tried to run your program and found that the quote character causing to split the cell value on each comma in the string. changing quote character to double quote worked for me
f = CSV.parse(File.read(csv_path_array[0]), :headers => true, :quote_char => '"')
curr_csv = CSV.parse(File.read(csv_file), :headers => true, :quote_char => '"')
This is the hash I'm trying to format
input = {"test_key"=>"test_value", "test_key2"=>"test_value2"}
And this is the expected result
"{\n\t\"test_key\" = \"test_value\";\n\t\"test_key2\" = \"test_value2\";\n}"
I have the following code so far
def format_hash(hash)
output = ""
hash.to_s.split(',').each do |k|
new_string = k + ';'
new_string.gsub!('=>', ' = ')
output += new_string
end
end
which gives me the this output
output = "{\"test_key\" = \"test_value\"; \"test_key2\" = \"test_value2\"};"
But I'm still struggling with adding the rest. Any ideas/suggestions?
input = {"test_key"=>"test_value", "test_key2"=>"test_value2"}
"{" << input.map { |k,v| "\n\t\"#{k}\" = \"#{v}\"" }.join(';') << ";\n}"
#=> "{\n\t\"test_key\" = \"test_value\";\n\t\"test_key2\" = \"test_value2\";\n}"
The steps are as follows.
a = input.map { |k,v| "\n\t\"#{k}\" = \"#{v}\"" }
#=> ["\n\t\"test_key\" = \"test_value\"", "\n\t\"test_key2\" = \"test_value2\""]
b = a.join(';')
#=> "\n\t\"test_key\" = \"test_value\";\n\t\"test_key2\" = \"test_value2\""
"{" << b << ";\n}"
#=> "{\n\t\"test_key\" = \"test_value\";\n\t\"test_key2\" = \"test_value2\";\n}"
input may contain any number of key-value pairs that adhere to the indicated pattern.
One starting point might be to use JSON formatter:
require 'json'
input = {"test_key"=>"test_value", "test_key2"=>"test_value2"}
JSON.pretty_generate(input)
=> "{\n \"test_key\": \"test_value\",\n \"test_key2\": \"test_value2\"\n}"
This has some subtle differences, since it looks like you use = as opposed to :. That said, perhaps it's easier to work from this than from what you have.
Working with JSON
JSON.pretty_generate(input).gsub(/:/,' =').gsub(/,(?=\n)/, ';').gsub(/(;\n|\n)\s+/, '\1'+"\t")
=> "{\n\t\"test_key\" = \"test_value\";\n\t\"test_key2\" = \"test_value2\"\n}"
Custom Formatter
Of course you could define your custom formatter:
def formatter(hash)
output = ""
output += "{\n\t"
output += hash.entries.map{|a| "\"#{a[0]}\" = \"#{a[1]}\"" }.join(";\n\t")
output += ";\n}"
end
formatter( input )
I have two solutions to reverse a string in Ruby. One prints true while the other prints false, however, both print out the response I want.
Why does one say it's false even though it results in the same answer as the solution that prints true?
Here are the solutions and the tests:
def reverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = new.to_s + string[-1, 1].to_s
string.chop!
if i >= string.length
break
end
end
puts new
end
def secondreverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = string[i] + new
i += 1
end
return new
end
These are tests to check that the code is working. After writing your solution, they should all print true.
puts("\nTests for #reverse")
puts("===============================================")
puts(
'secondreverse("abc") == "cba": ' + (secondreverse("abc") == "cba").to_s
)
puts(
'secondreverse("a") == "a": ' + (secondreverse("a") == "a").to_s
)
puts(
'secondreverse("") == "": ' + (secondreverse("") == "").to_s
)
puts("===============================================")
In your #reverse function, you are returning puts new when you should just be returning new.
As you can see from the example below, puts returns nil after it prints to the screen:
irb(main): puts 'test'
test
=> nil
If you change puts new to just new, it works as you expect.
Aside
You don't need to use explicit return calls. In Ruby, the last line executed will be returned, so you can replace this in both methods:
return new
with:
new
The problem is that in the reverse method, you are printing the value to stdout using the puts method but you are not returning it (your method returns nil instead). When you compare nil == "cba" it returns false. You have to return the new variable:
def reverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = new.to_s + string[-1, 1].to_s
string.chop!
if i >= string.length
break
end
end
new
end
I'm new to Ruby and I have a JSON data set that I am de-identifying using stympy's Faker. I would prefer to change the values in the Hash by reference.
I've tried changing the assignments eg. key['v] = namea[1] to data['cachedBook']['rows'][key][value] = namea[1] but I get a no implicit conversion of Array into String error. Which makes sense since each is an array in itself, but I'm unsure as to how proceed on this.
A single row e.g. data['cachedBook']['rows'] looks like this:
[{"v":"Sijpkes_PreviewUser","c":"LN","uid":"9######","iuid":"3####7","avail":true,"sortval":"Sijpkes_PreviewUser"},
{"v":"Paul","c":"FN","sortval":"Paul"},
{"v":"#####_previewuser","c":"UN"},
{"v":"","c":"SI"},{"v":"30 June 2016","c":"LA","sortval":1467261918000},
{"v":"Available","c":"AV"},[],[],[],[],[],[],
{"v":"-","tv":"","numAtt":"0","c":"374595"},[],[],
{"v":"-","tv":"","numAtt":"0","c":"374596"},[],[],[],
{"v":0,"tv":"0.0","mp":840,"or":"y","c":"362275"},
{"v":0,"tv":"0.0","mp":99.99999,"or":"y","c":"389721"}]
The key and value are interpreted as the first two entries.
Sensitive data has been removed with ####s.
Ruby code:
data['cachedBook']['rows'].each do |key, value|
fullname = Faker::Name.name
namea = fullname.split(' ')
str = "OLD: " + String(key['v']) + " " + String(value['v']) +"\n";
puts str
if ["Ms.", "Mr.", "Dr.", "Miss", "Mrs."].any? { |needle| fullname.include? needle }
key['v'] = namea[2]
value['v'] = namea[1]
value['sortval'] = namea[1]
else
key['v'] = namea[1]
value['v'] = namea[0]
value['sortval'] = namea[1]
end
str = "\nNEW: \nFullname: "+String(fullname)+"\nConverted surname: "+ String(key['v']) + "\n\t firstname: " + String(value['v'])
puts str
end
puts data
OK, this has been an excellent learning exercise!
The problem I was having was in two parts:
the JSON output from JSON.parse was a Hash, but the Hash was storing Arrays, so my code was breaking. Looking at the sample data rows above, it includes some empty arrays: ... [],[],[] ....
I misunderstood how each was working with a Hash, I assumed key, value (similar to jquery each) but the key, value in the original each statement actually evaluated to the first two array elements.
So here is my amended code:
data['cachedBook']['rows'].map! { |row|
fullname = Faker::Name.name
namea = fullname.split(' ')
row.each { |val|
if val.class == Hash
newval = val.clone
if ["Ms.", "Mr.", "Dr.", "Miss", "Mrs."].any? { |needle| fullname.include? needle }
if val.key?("c") && val["c"] == "LN"
newval["v"] = namea[1]
newval["sortval"] = namea[1]
end
if val.key?("c") && val["c"] == "FN"
newval["v"] = namea[2]
newval["sortval"] = namea[2]
end
else
if val.key?("c") && val["c"] == "LN"
newval["v"] = namea[0]
newval["sortval"] = namea[0]
end
if val.key?("c") && val["c"] == "FN"
newval["v"] = namea[1]
newval["sortval"] = namea[1]
end
end
val.merge!(newval)
end
}
}
I'm not a programmer, but I want to become one. So, I read books, I do tutorials and I ask questions. Here is the question:
I'm trying to do a Ruby quiz – the one with the Solitaire cypher (http://rubyquiz.com/quiz1.html). I wrote some code that works pretty well, except that at one point it alters the key_deck array which should be a reference to the end of the program. I do not know where this happens or why.
Here is my noobish code:
$characters = Array ('A' .. 'Z')
def encode to_encode, input_deck
trekljdfg = input_deck
edeck = input_deck
work_string = ''
to_encode.upcase.split("").each do |char|
if $characters.include?(char)
work_string.concat(char)
end
end
work_string = work_string + ('X' * ((5 - (work_string.length % 5)) % 5))
keystream_string = ''
while keystream_string.length < work_string.length do # <-- generate keystream
edeck = permutation(edeck)
keystream_string.concat(get_letter(edeck))
end
encoded = combine_with_keystream(work_string, keystream_string)
encoded = split_string_in_groups(encoded)
return encoded
end
def decode to_decode, input_deck
ddeck = input_deck
to_decode = to_decode.delete(' ')
keystream_string = ''
while keystream_string.length < to_decode.length do # <-- generate keystream
ddeck = permutation(ddeck)
keystream_string.concat(get_letter(ddeck))
end
array_to_decode = text_to_numbers to_decode
array_keystream_string = text_to_numbers keystream_string
decoded = ''
for i in 0..(array_to_decode.length-1)
if array_to_decode[i] >= array_keystream_string[i]
decoded.concat($characters[(array_to_decode[i] - array_keystream_string[i])-1])
else
decoded.concat($characters[(array_to_decode[i] + 26 - array_keystream_string[i])-1])
end
end
decoded = split_string_in_groups decoded
return decoded
end
def permutation deck_to_change
deck = deck_to_change
def swap array, joker
work_array = array
joker_position = work_array.index(joker)
if joker_position == (work_array.length-1)
temp_array = work_array.slice!(1..(work_array.length-2))
work_array = work_array + temp_array
else
work_array[joker_position], work_array[joker_position+1] = work_array[joker_position+1], work_array[joker_position]
end
return work_array
end
deck = swap(deck, 'A') # <-- swap first joker
2.times do # <-- swap second joker
deck = swap(deck, 'B')
end
if deck.index('A') < deck.index('B') # <-- triple cut
joker_position1 = deck.index('A')
joker_position2 = deck.index('B') - joker_position1
else
joker_position1 = deck.index('B')
joker_position2 = deck.index('A') - joker_position1
end
if joker_position1 == 0
temp_array1 = []
else
temp_array1 = deck.slice!(0..joker_position1-1)
end
if joker_position2 == deck.length-1
temp_array2 = []
else
temp_array2 = deck.slice!(joker_position2+1..deck.length-1)
end
deck = temp_array2 + deck + temp_array1
if (deck.last != 'A') | (deck.last != 'B') # <-- count cut
temp_array1 = deck.slice!(0..deck.last.to_i-1)
temp_array2 = deck.pop(1)
deck = deck + temp_array1 + temp_array2
end
return deck
end
def get_letter deck
first = deck.first
case first
when 'A'
first = '53'
when 'B'
first = '53'
end
if (deck[first.to_i] == 'A') | (deck[first.to_i] == 'B')
return ''
else
return $characters[((deck[first.to_i]).to_i-1) % 26]
end
end
def text_to_numbers text
array = []
text.upcase.split("").each do |char|
array.push($characters.index(char)+1)
end
return array
end
def combine_with_keystream string1, string2
temp_array1 = text_to_numbers string1
temp_array2 = text_to_numbers string2
string = ''
for i in 0..(temp_array1.length-1)
tmp = temp_array1[i] + temp_array2[i]
if tmp > 26
tmp = tmp - 26
end
string.concat($characters[tmp-1])
end
return string
end
def split_string_in_groups string
return string.scan(/.{1,5}/).join(" ")
end
#-begin-------------------------
key_deck = ('1' .. '52').to_a + ['A', 'B'] # <-- this is the key deck ^^
string_to_encode = 'Code in Ruby live longer!' # <-- this is the string to be encoded
string_to_decode = 'GLNCQ MJAFF FVOMB JIYCB' # <-- this is the string to be decoded
puts "Your encoded text is: #{encode(string_to_encode, key_deck)}"
puts "Your decoded text is: #{decode(string_to_decode, key_deck)}"
You are using slice! to find your deck's permutations, which changes the input array.
The easiest solution is to dup the array before working on it:
def encode to_encode, input_deck
trekljdfg = input_deck.dup
edeck = input_deck.dup
# ..
end
def decode to_decode, input_deck
ddeck = input_deck.dup
# ..
end
dup creates a copy of the array, which you can safely mutilate.
Your permutation function alters the deck. You pass it a reference to the original deck, so any changes you do on the reference will actually change the original. Try something like this instead:
key_deck = [....]
...
puts "#{encode(string_to_encode, key_dec.clone}"
The clone method will make a new copy of the array for you so all changes will only apply to the copy.
Or you can just avoit the entire problem by using the Array#shuffle method:
puts "#{encode(string_to_encode, key_dec.shuffle}"
That will give an already shuffled deck to the encode function.