I have a JSON with many records, for this question lets assume 100. There are two keys, Change and Sign. Change is something like 0.14 and Sign is one of two symbols "+" or "-".
For each one of the records, I would like to add two more keys "20 record up total" and "20 record down total". Where these equaled the sum of the last 20 records where the sign was "+" and "-" respectively.
So, for "20-record up total" something like:
array.collect {|array| array['change']}
array.collect {|array| array['sign']}
if sign = "+" then
#store change in uparray
#when uparray has 20 records add it up and create variable 20recorduptotal
Any help is appreciated. Thanks a lot
I believe I need to use .each_cons(20) but of only the records with the applicable sign.
Update: here's something that might actually work :)
I've defined a "summing queue" that calculates the sum of the last 20 items added to it. Its sum returns nil if it has fewer than 20 items.
class SummingQueue
def initialize(length = 20)
#values = []
#sum = 0
#length = length
end
def <<(value)
#values << value
#sum += value
#sum -= #values.shift if #values.length > #length
end
def sum
return nil if #values.length < #length
#sum
end
end
queues = Hash.new {|hash, key| hash[key] = SummingQueue.new }
array.each do |item|
queues[item["sign"]] << item["change"]
item["last_20_ups"] = queues["+"].sum
item["last_20_downs"] = queues["-"].sum
end
Note that the last_20_ups will include the current value (if applicable) - if you want it to be the last 20 values not including the current one, then move the queues[...] << item[...] line to the end of the each block.
Related
I'm completely new to ruby and wanted to ask for some help with this ruby script.
it's supposed to take in a string and find out which character occurs the most frequently. It does this using a hash, it stores all the characters in a hash and then iterates through it to find the one with greatest value. As of right now it doesn't seem to be working properly and i'm not sure why. It reads the characters in properly as far as i can tell with print statements. Any help is appreciated.
Thanks!
puts "Enter the string you want to search "
input = gets.chomp
charHash = Hash.new
input.split("").each do |i|
if charHash.has_key?(i)
puts "incrementing"
charHash[i]+=1
else
puts"storing"
charHash.store(i, 1)
end
end
goc = ""
max = 0
charHash.each { |key,value| goc = key if value > max }
puts "The character #{goc} occurs the most frequently"
There are two major issues with you code:
As commented by Holger Just, you have to use += 1 instead of ++
charHash.store(:i, 1) stores the symbol :i, you want to store i
Fixing these results in a working code (I'm using snake_case here):
char_hash = Hash.new
input.split("").each do |i|
if char_hash.has_key?(i)
char_hash[i] += 1
else
char_hash.store(i, 1)
end
end
You can omit the condition by using 0 as your default hash value and you can replace split("").each with each_char:
char_hash = Hash.new(0)
input.each_char do |i|
char_hash[i] += 1
end
Finally, you can pass the hash into the loop using Enumerator#with_object:
char_hash = input.each_char.with_object(Hash.new(0)) { |i, h| h[i] += 1 }
I might be missing something but it seems that instead of
charHash.each { |key,value| goc = key if value > max }
you need something like
charHash.each do |key,value|
if value > max then
max = value
goc = key
end
end
Notice the max = value statement. In your current implementation (i.e. without updating the max variable), every character that appears in the text at least once satisfies the condition and you end up getting the last one.
I'm trying to iterate a URL to scrape. What am I missing in my syntax?
array = [1...100]
array.each do |i|
a = 'http://www.web.com/page/#{i}/'.scrapify(images: [:png, :gif, :jpg])
extract_images(a[:images])
end
array = [1...100] doesn't do what you think it does. That creates an array with a single element and that single element is a Range instance whose first value is 1 and whose last value is 99.
So, after sorting out your string interpolation problem (as noted elsewhere), this:
"http://www.web.com/page/#{i}/"
will be the string:
"http://www.web.com/page/1...100/"
and the remote server probably doesn't know what that means and it will either 404 or give you page one; your comments elsewhere suggest that it will give you page one and ignore the ...100 part of the URL.
If you want it loop from 1 to 99 then you'd say:
(1...100).each do |i|
# `i` will range from 1 to 99 in this block
end
If you want to loop from 1 to 100 you'd use .. instead of ...:
(1..100).each do |i|
# `i` will range from 1 to 100 in this block
end
You could also ditch the range completely and use times:
99.times do |i|
# `i` will range from 0 to 98 in this block so
# you'd work with `i+1`
end
100.times do |i|
# `i` will range from 0 to 99 in this block so
# you'd work with `i+1`
end
or upto (thanks to JKillian for the reminder about this one):
1.upto(99) do |i|
# `i` will range from 1 to 99 in this block
end
1.upto(100) |i|
# `i` will range from 1 to 100 in this block
end
For interpolation you should use double quotes(" " instead ' '):
array = [1...100]
array.each do |i|
a = "http://www.web.com/page/#{i}/".scrapify(images: [:png, :gif, :jpg])
extract_images(a[:images])
end
I am mapping an array of items, but the collection can be quite large. I would like to put a message to console every so often, to give an indication of progress. Is there a way to do that during the mapping process?
This is my map statement:
famgui = family_items.map{|i|i.getGuid}
I have a def that I use for giving an update when I am doing a for each or while loop.
This is the def:
def doneloop(saymyname, i)
if (i%25000 == 0 )
puts "#{i} #{saymyname}"
end
end
I normally put x = 0 before I start the loop, then x +=1 once I am in the loop and then at the end of my loop, I put saymyname = "specific type items gathered at #{Time.now}"
Then I put doneloop(saymyname, x)
I am not sure how to do that when I am mapping, as there is no loop to construct this around. Does anyone have a method to give updates when using map?
Thanks!
You can map with index:
famgui = family_items.with_index.map {|item, index| item.getGuid; doneloop('sth', index)}
Only the last expression is returned from a map, so you can do something like:
famgui = family_items.with_index.map do |i, idx|
if idx % 100 == 0
puts # extra linefeed
# report every 100th round
puts "items left: #{family_items_size - idx}"
STDOUT.flush
end
current_item += 1
print "."
STDOUT.flush
i.getGuid
end
This will print "." for each item and a status report after every 100 items.
If you want, you can use each_with and populate the array yourself like:
famgui = []
family_items.each_with_index do |i, idx|
famgui << i.getGuid
puts "just did: #{idx} of #{family_items.size}"
end
I have the following:
ht = Hash.new {|h,k| h[k]=[]}
CertainParentClass.decendants.each do {|childrens|
ht[childrens] << incremented_integer
}
$global_switch[0 ... ht.size] = false
def check_switch(object_class)
return $global_switch[ht[object_class][0]]
end
And I'd like to know how to do incremented_integer in ruby or if there's a better solution rather than inserting an integer to check the position value, like key_position that would be great!
def check_switch(object_class)
return $global_switch[ht[object_class].key_position]
end
I think something below using Enumerable#each_with_index:
CertainParentClass.decendants.each_with_index do |childrens,ind|
ht[childrens] << ind # ind will increment with each pass by 1 default.
end
In case of #each_with_index, ind value will bet set as 0 by default. Now if you want to set it with other numbers rather than 0, use Enumerator#with_index. Like below
CertainParentClass.decendants.each.with_index(2) do |childrens,ind|
ht[childrens] << ind # ind will increment with each pass by 1 default.
end
I passed 2 as initial argument, which is the first ind value in the with_index(2) method, you can change it as per your need
You can use modified iterator :with_index of Enumerable module, which added index number to the end of block argument list, do as follows:
CertainParentClass.decendants.each.with_index do |child, index|
ht[ childrens ] << index
end
And of couse you can modify not only :each iterator method, but a method, which returns modified, or non modified enumerable. Those methods are :map, :reject, :select.
I have a ruby problem
Here's what i'm trying to do
def iterate1 #define method in given class
#var3 = #var2.split(" ") #split string to array
#var4 = #var3
#var4.each do |i| #for each array item do i
ra = []
i.each_char {|d| ra << counter1(d)} # for each char in i, apply def counter1
#sum = ra.inject(:+)
#sum2 = #sum.inject(:+) #have to do the inject twice to get values
end
#sum2
I know i have over complicated this
Basically the input is a string of letters and values like "14556 this word 398"
I am trying to sum the numbers in each value, seperated by the whitespace like (" ")
When i use the def iterate1 method the block calls the counter1 method just fine, but i can only get the value for the last word or value in the string.
In this case that's 398, which when summed would be 27.
If i include a break i get the first value, which would be 21.
I'm looking to output an array with all of the summed values
Any help would be greatly appreciated
I think you're after:
"10 d 20 c".scan(/\b\d+\b/).map(&:to_i).inject(:+) # Returns 30
scan(/\b\d+\b/) will extract all numbers that are made up of digits only in an array, map(&:to_i) will convert them to integers and I guess you already know what inject(:+) will do.
I'm not sure if I understand what you're after correctly, though, so it might help if you provide the answer you expect to this input.
EDIT:
If you want to sum the digits in each number, you can do it with:
"12 d 34 c".scan(/\b\d+\b/).map { |x| x.chars.map(&:to_i).inject(:+) }
x.chars will return an enumerator for the digits, map(&:to_i) will convert them to integers and inject(:+) will sum them.
The simplest answer is to use map instead of each because the former collects the results and returns an array. e.g:
def iterate1 #define method in given class
#var3 = #var2.split(" ") #split string to array
#var4 = #var3
#var4.map do |i| #for each array item do i
ra = []
i.each_char {|d| ra << counter1(d)} # for each char in i, apply def counter1
#sum = ra.inject(:+)
#sum2 = #sum.inject(:+) #have to do the inject twice to get values
end
end
You could write it a lot cleaner though and I think Stefan was a big help. You could solve the issue with a little modification of his code
# when you call iterate, you should pass in the value
# even if you have an instance variable available (e.g. #var2)
def iterate(thing)
thing.scan(/\b\d+\b/).map do |x|
x.chars.map{|d| counter1(d)}.inject(:+)
end
end
The above assumes that the counter1 method returns back the value as an integer