I've been working on a task to create a function that returns the total number of smiley faces. Valid smiley faces look like: ":) :D ;-D :~)" and invalid smiling faces: ";( :> :} :] ".
My solution below throws the following error message "expected 0 instead got 1".
def count_smileys(arr)
arr.count do |element|
[":)", ":D", ";-D", ":~)", ";~D", "8~(", ";(", ":>", ":}", ":]",
"8~P", "8-(", "; )", ";-P", ":~P", "~P", ":~P", "~P", "; (", ":-)",
"8~D", "~)", "8D", "~)", "8 )", "; )", "~)", ":-D", " (", ";D",
"8-D", "8-P", ";-D", ": D", ";~D", " ("].include?(element)
end
end
As it stands my solution passes 4 out of 5 basic tests:
Basic tests
Test Passed: Value == 0
Test Passed: Value == 4
Test Passed: Value == 2
Test Passed: Value == 1
Expected: 0, instead got: 1
I've tried removing the parameters but that only worked for the last test and failed me on the rest. Any suggestions? Thanks
Example tests below:
Test.describe("Basic tests") do
Test.assert_equals(count_smileys([]), 0)
Test.assert_equals(count_smileys([":D",":~)",";~D",":)"]), 4)
Test.assert_equals(count_smileys([":)",":(",":D",":O",":;"]), 2)
Test.assert_equals(count_smileys([";]", ":[", ";*", ":$", ";-D"]), 1)
Test.assert_equals(count_smileys([";", ")", ";*", ":$", "8-D"]), 0)
end
Compare or Inspect Using Array Intersection
You don't show your actual test setup, so I won't address that. However, it seems like you're trying to count the number of smileys in a given array using the block form of Array#count. It's arguably simpler to measure the size of an Array intersection, or to inspect the contents of the intersection, using Array#&. For example:
SMILEYS = [
":)", ":D", ";-D", ":~)", ";~D", "8~(", ";(", ":>", ":}", ":]",
"8~P", "8-(", "; )", ";-P", ":~P", "~P", ":~P", "~P", "; (", ":-)",
"8~D", "~)", "8D", "~)", "8 )", "; )", "~)", ":-D", " (", ";D",
"8-D", "8-P", ";-D", ": D", ";~D", " ("
]
SMILEYS.&([":D", ":~)", ";~D", ":)"]).size == 4
#=> true
SMILEYS.&([":)", ":(", ":D", ":O", ":;"]).size == 2
#=> true
SMILEYS.&([";]", ":[", ";*", ":$", ";-D"]).size == 1
#=> true
SMILEYS.&([";", ")", ";*", ":$", "8-D"]).size == 0
#=> false
Note that the last comparison is false because 8-D is defined in your list of SMILEYS. Specifically:
SMILEYS.&([";", ")", ";*", ":$", "8-D"])
#=> ["8-D"]
so the expected value should be 1 unless 8-D doesn't really belong in your array of acceptable values. If that's the case, remove it from the variable or constant containing your allowable values.
Related
As part of sorting based on priority_id column label names, I have done the below code:
products = Product.where(id: pr_ids).order("priority_id IN(?)",ordered_priority_ids)
The below error is showing:
ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR: syntax error at or near ")"
LINE 1: ...ducts"."id" IN ($2, $3) ORDER BY priority_id IN(?), 4, 2, 3...
^
):
Please help.
Thanks
def self.order_by_priority_ids(ids)
return self.where(:id => 0) if ids.blank?
values = []
ids.each_with_index do |priority_id, index|
values << "(#{priority_id}, #{index + 1})"
end
return self.joins("JOIN (VALUES #{values.join(",")}) as x (priority_id, ordering) ON #{table_name}.priority_id = x.priority_id").reorder('x.ordering')
end
And then you can use:
Product.where(id: pr_ids).order_by_priority_ids(ordered_priority_ids)
I was wondering how I'm supposed to get my "balance" variable to change depending on the value added or subtracted to it each time, without resetting for the next loop.
At the moment it will add or subtract a given value and return the correct number but on the next loop it will use the original base value instead of using the new value from the previous loop.
import time
print ("Welcome to balance tracker!")
sBal=float(input ("Starting balance: "))
float(sBal)
transactionAmounts = []
transactionTypes = []
print ("Your current balance is", '$',sBal)
while True:
input("Press enter to continue")
print ("""
------------
[A]ddition
[S]ubtraction
[H]istory
[I]nformation
[Q]uit
------------
""")
choice=input("What would you like to do? ")
choice = choice.upper()
if choice == "A":
aval=float(input ("Enter amount you would like to add here: "))
nBal=(sBal)+(aval)
transactionTypes.append('Addition')
transactionAmounts.append(float(aval))
balance = float(sBal) + aval
print ("Your current balance is $",balance)
elif choice == "S":
aval=input("Enter amount you would like to subtract here: ")
nBal=float(sBal)-float(aval)
transactionTypes.append('Subtraction')
transactionAmounts.append (float(aval))
balance = float(sBal) - aval
print ("Your current balance is $",balance)
elif choice == "H":
aCount = 1
tCount = 0
for i in transactionAmounts:
print ('Transaction',aCount,':',transactionTypes[tCount],i)
aCount = aCount + 1
tCount = tCount + 1
elif choice == "Q":
break
else:
print ("invalid choice")
The solution to your problem is that when your assigning your balance variable, your adding sBal(starting balance) every time to the amount they would like to add... This is causing the balance to always be set back to the starting balance plus or minus the amount added. Try defining sBal as balance also like so...
balance = float(input("Starting balance: ")) #line 5
float(sBal) #<<<can be deleted #line 6
Also don't forget to alter anywhere in the code that calls sBal to call balance.
print("Your current balance is ", "$", difference) #line 9
balance = float(balance) (+ or -) aval #lines 30 and 37
Also you can delete your nBal(new balance) as it is never called.
I have these Syslog messages:
N 4000000 PROD 15307 23:58:12.13 JOB78035 00000000 $HASP395 GGIVJS27 ENDED\r
NI0000000 PROD 15307 23:58:13.41 STC81508 00000200 $A J78036 /* CA-JOBTRAC JOB RELEASE */\r
I would like to parse these messages into various fields in a Hash, e.g.:
event['recordtype'] #=> "N"
event['routingcode'] #=> "4000000"
event['systemname'] #=> "PROD"
event['datetime'] #=> "15307 23:58:12.13"
event['jobid'] #=> "JOB78035"
event['flag'] #=> "00000000"
event['messageid'] #=> "$HASP395"
event['logmessage'] #=> "$HASP395 GGIVJS27 ENDED\r"
This is the code I have currently:
message = event["message"];
if message.to_s != "" then
if message[2] == " " then
array = message.split(%Q[ ]);
event[%q[recordtype]] = array[0];
event[%q[routingcode]] = array[1];
event[%q[systemname]] = array[2];
event[%q[datetime]] = array[3] + " " +array[4];
event[%q[jobid]] = message[38,8];
event[%q[flags]] = message[47,8];
event[%q[messageid]] = message[57,8];
event[%q[logmessage]] = message[56..-1];
else
array = message.split(%Q[ ]);
event[%q[recordtype]] = array[0][0,2];
event[%q[routingcode]] = array[0][2..-1];
event[%q[systemname]] = array[1];
event[%q[datetime]] = array[2] + " "+array[3];
event[%q[jobid]] = message[38,8];
event[%q[flags]] = message[47,8];
event[%q[messageid]] = message[57,8];
event[%q[logmessage]] = message[56..-1];
end
end
I'm looking to improve the above code. I think I could use a regular expression, but I don't know how to approach it.
You can't use split(' ') or a default split to process your fields because you are dealing with columnar data that has fields that have no whitespace between them, resulting in your array being off. Instead, you have to pick apart each record by columns.
There are many ways to do that but the simplest and probably fastest, is indexing into a string and grabbing n characters:
'foo'[0, 1] # => "f"
'foo'[1, 2] # => "oo"
The first means "starting at index 0 in the string, grab one character." The second means "starting at index 1 in the string, grab two characters."
Alternately, you could tell Ruby to extract by ranges:
'foo'[0 .. 0] # => "f"
'foo'[1 .. 2] # => "oo"
These are documented in the String class.
This makes writing code that's easily understood:
record_type = message[ 0 .. 1 ].rstrip
routing_code = message[ 2 .. 8 ]
system_name = message[ 10 .. 17 ]
Once you have your fields captured add them to a hash:
{
'recordtype' => record_type,
'routingcode' => routing_code,
'systemname' => system_name,
'datetime' => date_time,
'jobid' => job_id,
'flags' => flags,
'messageid' => message_id,
'logmessage' => log_message,
}
While you could use a regular expression there's not much gained using one, it's just another way of doing it. If you were picking data out of free-form text it'd be more useful, but in columnar data it tends to result in visual noise that makes maintenance more difficult. I'd recommend simply determining your columns then cutting the data you need based on those from each line.
I am trying to eval a string with undefined variable. For example: Formula = 2 * 3 + a The result should return a string of 6 + a. Can the eval method do something like that? Or, can you give me some ideas on how to do this?
Update: Thank you for all the inputs. I guess this is not as simple as i thought it would be. Let's say if I don't need to simplify the formula and all i need to do is to replace the variable with value in string?
Example:
a = { "Bob" => 82,
"Jim" => 94,
"Billy" => 58, ........ and more}
How do I convert this string
"2 * 3 + a["Bob"] * b"
to this: "2 * 3 + 82 * b"
Thanks again for your help.
What you are trying to do is complicated, and I don't think it is worth doing it.
You can use some gem to parse the string into a tree of tokens. Then, look for any node under which there is no undefined variable, and replace the node with the calculated value. After doing that, put the tree back to a string.
What about using something like Parslet and making a parsing expression grammar to simplify your expressions? Look at the get started page where you are walked through a simple example in which Parslet reduces integer expressions.
Here is a naïve solution:
str = "+ 1 * 2 - 3 / 2 + b - a"
terms = str.gsub(/^[^+-]+|[+-][^+-]+/).to_a
=> ["+ 1 * 2 ",
"- 3 ",
"+ b ",
"- a"]
numbers, variables = terms.partition { |exp| eval(exp.to_s) rescue false }
=> [["+ 1 * 2 ", "- 3 "],
["+ b ", "- a"]]
numbers.map! { |exp| exp.gsub(/\d+/, &:to_f) }
=> ["+ 1.0 * 2.0 ",
"- 3.0 / 2.0 "]
numbers.map! { |exp| eval(exp) }
=> [2.0, -1.5]
sum = numbers.reduce(:+)
=> "0.5"
result = "#{sum} #{variables.join}"
=> "0.5 + b - a"
I have a code that parses through text files in a folder, and saves a predefined number of words around certain search words.
For example, it looks for words such as "date" and "year". If it finds both in the same sentence it will save the sentence twice. Furthermore, if it finds the same word used a few times in a sentence, it will also save it multiple times.
This way the scraper saves an enormous amount of unnecessary duplicate text.
I see two possible solutions:
If the next search-match is in the padding, in the group of words, of the previous one, it will not be saved.
If a group of, say, seven words of the search match is also part of the preceding group it will not be saved/deleted.
Everything I've tried has utterly failed thusfar:
#helper
def indices text, index, word
padding = 200
bottom_i = index - padding < 0 ? 0 : index - padding
top_i = index + word.length + padding > text.length ? text.length : index + word.length + padding
return bottom_i, top_i
end
#script
base_text = File.open("base.txt", 'w')
Dir::mkdir("summaries") unless File.exists?("summaries")
Dir.chdir("summaries")
Dir.glob("*.txt").each do |textfile|
whole_file = File.open(textfile, 'r').read
puts "Currently summarizing " + textfile + "..."
curr_i = 0
str = nil
whole_file.scan(Regexp.union(/firstword/, /secondword/).each do |match|
if i_match = whole_file.index(match, curr_i)
top_bottom = indices(whole_file, i_match, match)
base_text.puts(whole_file[top_bottom[0]..top_bottom[1]] + " : " + File.path(textfile))
curr_i += i_match
end
end
puts "Done summarizing " + textfile + "."
end
base_text.close
Why not do something where you keep track of what you're looking for:
search_words = %w( year date etc )
And then downcase the search string, and start an index.
def summarize(str)
search_str = str.downcase
ind = 0
Then find the smallest index offset of your search words in the search_str, and remove everything up to (ind + offset - delta), go up to (ind + delta) into matches, and continue in the while loop. Something like:
matches = []
while (offset = search_words.map{|w| search_str.index w }.min)
ind += offset
matches.push str[ind - delta, delta * 2]
search_str = search_str[offset + delta, ]
end
matches
end
Preferably something better than:
whole_file.scan(Regexp.union(/firstword/, /secondword/).each do |match|
if i_match = whole_file.index(match, curr_i)
top_bottom = indices(whole_file, i_match, match)
base_text.puts(whole_file[top_bottom[0]..top_bottom[1]] + " : " + File.path(textfile))
curr_i += i_match + 50
end
end