Count from json data Rails - ruby-on-rails

I'm looking to count from some json data but it's outputting:
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
View
<%= #overall %>
Where the 1's are greater than 40. instead of '2'.
json data is formatted as from a url:
{"status": "ok", "data": [{"2014-06-16": 32.1},{"2014-06-17": 30.2},{"2014-06-18": 42.9}]} etc
Controller
#data = JSON.parse(open(#temperature.url).read)
#overall = []
#data['data'].each do |data|
dates << data.keys
temps << data.values
#overall << data.values.count { |i| i > 40 }
end

Since JSON data: is an array I am assuming that multiple dates are represented by multiple hashes (one for each day). Is this correct?
{"status": "ok", "data": [{"2014-06-16": 42.1}, {"2014-06-17": 45.5}]
If that's the case, this should work:
#data = JSON.parse(open(#temperature.url).read)
dates = #data['data'].map {|data| data.keys.first}
temps = #data['data'].map {|data| data.values.first}
#overall = temps.count {|temp| temp > 40}

Ok - this wil solve the issue
#data = JSON.parse(open(#temperature.url).read)
#overall = []
#data['data'].each do |data|
dates << data.keys
temps << data.values
end
forty_count = temps.flatten.count {|i| i > 40 }
the problem with your above code is that you can't do a count on-the-fly... you can only count when you have the full set of temperatures - which only happens when you get to the end.
also the way you are adding the "data.values" to the temps array, makes it into an array of arrays, which you can see if you do it this way:
data = [{"2014-06-16" => 32.1},{"2014-06-17" => 30.2},{"2014-06-18" => 42.9}]
data.each do |data|
temps << data.values
end
puts temps.inspect # [[32.1], [30.2], [42.9]]
puts temps.flatten.inspect # [32.1, 30.2, 42.9]
temps.count {|i| i > 40 } # explodes!
temps.flatten.count {|i| i > 40 } # 1

Related

Ruby -- group items by day with shifted beginning of the day

I have a dictionary as such:
{"status": "ok", "data": [{"temp": 22, "datetime": "20160815-0330"}]}
20160815-0330 means 2016-08-15 03:30. I want to group by day and calculate min and max temp, but the day should start at 09:00 and end at 08:59. How could I do that?
Here is my code:
#results = #data['data'].group_by { |d| d['datetime'].split('-')[0] }.map do |date, values|
[date, {
min_temp: values.min_by { |value| value['temp'] || 0 }['temp'],
max_temp: values.max_by { |value| value['temp'] || 0 }['temp']
}]
end
#=> {"20160815"=>{:min_temp =>12, :max_temp =>34}}
I works, but the starting point is 00:00, not 09:00.
I would suggest to use a timezone trick:
def adjust_datetime(str)
DateTime.parse(str.sub('-', '') + '+0900').utc.strftime('%Y%m%d')
end
data.group_by { |d| adjust_datetime(d['datetime']) }
Here is an explanation:
str = '20160815-0330'
str = str.sub('-', '') #=> '201608150330'
str = str + '+0900' #=> '201608150330+0900'
dt = DateTime.parse(str) #=> Mon, 15 Aug 2016 03:30:00 +0900
dt = dt.utc #=> 2016-08-14 18:30:00 UTC
dt.strftime('%Y%d%m') #=> '20160814'

Unexpected result determining coordinates on a nested (grid) array in rails

I have a nested array representing an "image" (a map of 0's and 1's). My end goal is to transform the 4 numbers surrounding any "1" to also be 1's.
The approach I've taken is to map the x,y coordinates of any existing 1 in the initial grid and add those coordinates to a new array so I can later use them to perform the transformation on the original.
To simplify, I'm trying to get this to work, initially, on an array that includes only one "1" — however, the result is unexpected in that it's storing multiple sets of x,y coordinates for the one "1" in the array, instead of a single set. I'm sure the solution is simple, but as a beginner, I'm stumped as to why this is happening.
(Please ignore the commented code; it's the beginnings of the transformation, but I'll bring it back once I solve this issue.)
class Image
def initialize(image)
#image = image
end
def output_image
x_size = #image.first.length
y_size = #image.length
edit = []
#image.each_with_index do | row , y |
row.each_with_index do |cell, x |
edit << [x,y] if cell == 1
end
# edit.each do |pair|
# x = pair.first
# y = pair.last
# #image[x-1][y] = 1 if x > 0
# #image[x+1][y] = 1 if x < (x_size - 1)
# #image[x][y-1] = 1 if y > 0
# #image[x][y+1] = 1 if y < (y_size - 1)
# end
puts edit.inspect
#puts #image.inspect
# #image.each { |x| puts x.join }
end
end
end
image = Image.new([
[0, 0, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
])
image.output_image
This results in:
[]
[[2, 1]]
[[2, 1]]
[[2, 1]]
Rather than the expected:
[]
[[2,1]]
[]
[]
You're not clearing the edit variable before each run of the outer loop. So each time you print it, it's still storing the initial coordinate where you found a 1.
You'll get the expected result if you insert edit = [] after puts edit.inspect.
This does what you want:
def output_image
x_size = #image.first.length
y_size = #image.length
edit = []
#image.each_with_index do | row , y |
row.each_with_index do |cell, x |
edit << [x,y] if cell == 1
end
end
edit.each do |pair|
y = pair.first
x = pair.last
#image[x-1][y] = 1 if x > 0
#image[x+1][y] = 1 if x < (x_size - 1)
#image[x][y-1] = 1 if y > 0
#image[x][y+1] = 1 if y < (y_size - 1)
end
#image.each { |x| puts x.join }
puts edit.inspect
end
Thanks again to Rob for the help. This was the solution I was looking for:
def output_image
x_size = #image.first.length
y_size = #image.length
edit = []
#image.each_with_index do | row , x |
row.each_with_index do |cell, y |
edit << [x,y] if cell == 1
end
end
edit.each do |x,y|
#image[x-1][y] = 1 if x > 0
#image[x+1][y] = 1 if x < (x_size - 1)
#image[x][y-1] = 1 if y > 0
#image[x][y+1] = 1 if y < (y_size - 1)
end
#image.each { |x| puts x.join }
end

Creating a range from one column

I have a column called "Marks" which contains values like
Marks = [100,200,150,157,....]
I need to assign Grades to those marks using the following key
<25=0, <75=1, <125=2, <250=3, <500=4, >500=5
If Marks < 25, then Grade = 0, if marks < 75 then grade = 1.
I can sort the results and find the first record that matches using Ruby's find function. Is it the best method ? Or is there a way by which I can prepare a range using the key by adding Lower Limit and Upper Limit columns to the table and by populating those ranges using the key? Marks can have decimals too Ex: 99.99
Without using Rails, you could do it like this:
marks = [100, 200, 150, 157, 692, 12]
marks_to_grade = { 25=>0, 75=>1, 125=>2, 250=>3, 500=>4, Float::INFINITY=>5 }
Hash[marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }]
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
With Ruby 2.1, you could write this:
marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }.to_h
Here's what's happening:
Enumerable#map (a.k.a collect) converts each mark m to an array [m, g], where g is the grade computed for that mark. For example, when map passes the first element of marks into its block, we have:
m = 100
a = marks_to_grade.find { |k,_| m <= k }
#=> marks_to_grade.find { |k,_| 100 <= k }
#=> [125, 2]
a.last
#=> 2
so the mark 100 is mapped to [100, 2]. (I've replaced the block variable for the value of the key-value pair with the placeholder _ to draw attention to the fact that the value is not being used in the calculation within the block. One could also use, say, _v as the placeholder.) The remaining marks are similarly mapped, resulting in:
b = marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }
#=> [[100, 2], [200, 3], [150, 3], [157, 3], [692, 5], [12, 0]]
Lastly
Hash[b]
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
or, for Ruby 2.1+
b.to_h
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
You can make use of update_all:
Student.where(:mark => 0...25).update_all(grade: 0)
Student.where(:mark => 25...75).update_all(grade: 1)
Student.where(:mark => 75...125).update_all(grade: 2)
Student.where(:mark => 125...250).update_all(grade: 3)
Student.where(:mark => 250...500).update_all(grade: 4)
Student.where("mark > ?", 500).update_all(grade: 5)

I'd like to add to an array from a do block. How is this done?

Here is the array, do block and my attempts to write into the new arrays:
#event_class_array is dynamic, and does not always have the same arrays, but the format is consistant.
#rule_name = Array.new
#rule_count = Array.new
#event_class_array = [["WEB-APPLICATION-ATTACK", 1222], ["MISC-ACTIVITY", 6], ["ATTEMPTED-ADMIN", 6], ["POLICY-VIOLATION", 5]]
#event_class_array.each do |exploit,count| # I also tried .collect with the same results
#rule_name = [exploit]
#rule_count = [count]
end
I've also tried this, but it writes out "WEB-APPLICATION-ATTACK" 1222 times, as well as the others:
#rule_name += [exploit]
#rule_count += [count]
same result as:
#rule_name << [exploit]
#rule_count << [count]
I would like for #rule_name and #rule_count to end up like this:
#rule_name = [["WEB-APPLICATION-ATTACK"], ["MISC-ACTIVITY"], ["ATTEMPTED-ADMIN"], ["POLICY-VIOLATION"]]
#rule_count = [[1222], [6], [6], [5]]
How about:
#event_class_array = [["WEB-APPLICATION-ATTACK", 1222], ["MISC-ACTIVITY", 6], ["ATTEMPTED-ADMIN", 6], ["POLICY-VIOLATION", 5]]
#rule_name, #rule_count = #event_class_array.transpose
#foo = Array.new
#bar = Array.new
easy:
#foo_bar.each do |x|
#foo << x[0]
#bar << x[1]
end
to ensure the same index
#foo_bar.each_with_index { |x, i|
#foo[i] = x[0]
#bar[i] = x[1]
}
Use this:
#rule_name = Array.new
#rule_count = Array.new
#event_class_array.each do |exploit|
#rule_name << [exploit.first]
#rule_count << [exploit.last]
end
OR
#rule_name = Array.new
#rule_count = Array.new
#event_class_array.each do |exploit, count|
#rule_name << [exploit]
#rule_count << [count]
end

Can I iterate through an array during a comparison?

s = Array.new
s << 19
while (s.last + 19) < 100000 do
s << s.last + 19
end
This^ works. s is an array of all factors of 19 below 100,000.
I'm trying to, in a succinct statement, find all numbers in s where the reverse of that number is also in the array. Ex: 176 and 671.
reflections= s.select { |num| num.to_s.reverse == s.each.to_s }
I know this is wrong, but how can I check each reversed item against the entire array?
This should work:
reflections = s.select { |num| s.include?(num.to_s.reverse.to_i) }
Although it produces results that you probably didn't anticipate
s = [176, 234, 671, 111]
reflections = s.select { |num| s.include?(num.to_s.reverse.to_i) }
reflections # => [176, 671, 111]
These are all valid results according to your logic.
Excluding self-match is pretty straighforward:
s = [176, 234, 671, 111]
reflections = s.select do |x|
x = x.to_s
r = x.reverse
(x != r) && s.include?(r.to_i)
end
reflections # => [176, 671]
reflections = s & s.map{|num| num.to_s.reverse.to_i}
Try:
reverse_array = s.select {|num| num.to_s == num.to_s.reverse }
UPDATE:
After checking I found this will work:
myarr = ""
s = (1..1000)
s.select{ |num|
unless s.include?(num.to_s.reverse.to_i)
myarr << num.to_s
end
}
Finally, the myarr will contain all the numbers whose reverse is present in array s.

Resources