Related
So I am just trying something simple, trying to add up all of the ages in my array using a loop. But for some reason it doesn't work when I type it out like this, what is the issue here? Laymans terms please since I am at the very beginning of my code journey.
ages = Array.new
ages[0] = 25
ages[1] = 22.4
ages[3] = 16.3
ages[4] = 21
ages[5] = 58.34
ages[6] = 33.25
total_years = 0.0
ages.each do |age|
total_years += age
end
It works when I type the array out like below
ages =[25, 22.4, 16.3, 21, 58.34, 33.25]
Any help would be much appreciated !
You've skipped an index in your array, thus giving it a nil value that cannot be used in arithmetic.
Take a look at how your array looks after you populate it:
ages = Array.new
ages[0] = 25
ages[1] = 22.4
ages[3] = 16.3
ages[4] = 21
ages[5] = 58.34
ages[6] = 33.25
This gives you an array that looks like:
[25, 22.4, nil, 16.3, 21, 58.34, 33.25]
Now when you attempt to add the ages together, it looks like this:
25 + 22.4 + nil + 16.3 + 21 + 58.34 + 33.25
What happens when you add a Float and nil together?
TypeError: nil can't be coerced into Float
So make sure your array is fully populated, then try adding it together.
One way to avoid this error is to populate your array like this:
ages = [25, 22.4, 16.3, 21, 58.34, 33.25]
That way you don't have to worry about using the wrong index number when populating it.
Finally, you can add these values together much more easily using the Array sum method:
ages = [25, 22.4, 16.3, 21, 58.34, 33.25]
total_years = ages.sum
This returns:
176.29
In the following code:
x = BigDecimal(10)
s = x.inspect # "#<BigDecimal:6fe4790,'0.1E2',9(36)>"
Is there a way to parse s and get the original value ? The reason is that I have some text files with BigDecimal written in them using inspect, and I need to parse these values.
You .to_s to get the value in string. .inspect will print the object
x = BigDecimal(10)
x.to_s
# => "0.1E2"
The documentation for BigDecimal#inspect is incomplete. Consider the following:
require 'bigdecimal`
BigDecimal.new("1.2345").inspect
#=> "#<BigDecimal:7fb06a110298,'0.12345E1',18(18)>"
...
BigDecimal.new("1.234567890").inspect
#=> "#<BigDecimal:7fb06a16ab58,'0.123456789E1',18(27)>"
BigDecimal.new("1.2345678901").inspect
#=> "#<BigDecimal:7fb06a14a6a0,'0.1234567890 1E1',27(27)>"
BigDecimal.new("1.23456789012").inspect
#=> "#<BigDecimal:7fb06a1393a0,'0.1234567890 12E1',27(27)>"
BigDecimal.new("1.234567890123").inspect
#=> "#<BigDecimal:7fb06a123780,'0.1234567890 123E1',27(27)>"
It can be seen from the source code for inspect that, if there are more than 10 significant digits, each 10 characters are separate by a space (for readability, presumably):
BigDecimal.new("123.456789012345678901234567").inspect
#=> "#<BigDecimal:7fb06a0ac8b0,'0.1234567890 1234567890 1234567E3',36(36)>"
I suggest retrieving the string representation of the BigDecimal value as follows:
str = "#<BigDecimal:7fb06a14a6a0,'0.1234567890 1E1',27(27)>"
str.delete(' ').split(?')[1]
#=> "0.12345678901E1"
We are not finished. We must still convert the string we extract to a numerical object. We cannot use BigDecimal#to_f, however, if the value is large in absolute value:
"1.23456789012345678".to_f
#=> 1.2345678901234567
The safest course of action is to return a BigDecimal object, using the method BigDecimal::new, which takes two arguments:
the value to be converted to a BigDecimal object, which can be an Integer, Float, Rational, BigDecimal, or String. If a String, which is what we will supply, "spaces are ignored and unrecognized characters terminate the value" (similar to "123.4cat".to_f #=> 123.4).
the number of significant digits. If omitted or zero, the number of significant digits is determined from the value. I will omit this argument. (For example, BigDecimal.new("0.1234E2").precs #=> [18, 18], where the array contains the current and maximum numbers of significant digits.
Note the second argument is required if the first is a Float or Rational, else it is optional.
We therefore can write:
require 'bigdecimal'
def convert(str)
BigDecimal.new(str.delete(' ').split(?')[1])
end
convert "#<BigDecimal:7facd39d7ee8,'0.1234E4',9(18)>"
#=> #<BigDecimal:7facd39c7de0,'0.1234E4',9(18)>
convert "#<BigDecimal:7facd39b7be8,'0.1234E2',18(18)>"
#=> #<BigDecimal:7facd39ae610,'0.1234E2',18(18)>
convert "#<BigDecimal:7facd3990638,'0.1234E0',9(18)>"
#=> #<BigDecimal:7facd3980aa8,'0.1234E0',9(18)>
convert "#<BigDecimal:7facd3970e28,'0.1234E-2',9(18)>"
#=> #<BigDecimal:7facd39625d0,'0.1234E-2',9(18)>
v = convert "#<BigDecimal:7fb06a123780,'0.1234567890 123E1',27(27)>"
#=> #<BigDecimal:7fb069851d78,'0.1234567890 123E1',27(27)>
An easy way to see if the BigDecimal object can be converted to a float without loss of accuracy is:
def convert_bd_to_float(bd)
f = bd.to_f
(bd==BigDecimal.new(f.to_s)) ? f : nil
end
convert_bd_to_float BigDecimal.new('1234567890123456')
#=> 1.234567890123456e+15
convert_bd_to_float BigDecimal.new('12345678901234567')
#=> nil
"#<BigDecimal:6fe4790,'0.1E2',9(36)>"[/(?<=').+(?=')/]
# => "0.1E2"
I don't know which version of Ruby you are using, so I checked some MRI source code for BigDecimal:
2000 /* Returns debugging information about the value as a string of comma-separated
2001 * values in angle brackets with a leading #:
2002 *
2003 * BigDecimal.new("1234.5678").inspect ->
2004 * "#<BigDecimal:b7ea1130,'0.12345678E4',8(12)>"
2005 *
2006 * The first part is the address, the second is the value as a string, and
2007 * the final part ss(mm) is the current number of significant digits and the
2008 * maximum number of significant digits, respectively.
2009 */
2010 static VALUE
2011 BigDecimal_inspect(VALUE self)
2012 {
2013 ENTER(5);
2014 Real *vp;
2015 volatile VALUE obj;
2016 size_t nc;
2017 char *psz, *tmp;
2018
2019 GUARD_OBJ(vp, GetVpValue(self, 1));
2020 nc = VpNumOfChars(vp, "E");
2021 nc += (nc + 9) / 10;
2022
2023 obj = rb_str_new(0, nc+256);
2024 psz = RSTRING_PTR(obj);
2025 sprintf(psz, "#<BigDecimal:%"PRIxVALUE",'", self);
2026 tmp = psz + strlen(psz);
2027 VpToString(vp, tmp, 10, 0);
2028 tmp += strlen(tmp);
2029 sprintf(tmp, "',%"PRIuSIZE"(%"PRIuSIZE")>", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig());
2030 rb_str_resize(obj, strlen(psz));
2031 return obj;
2032 }
2033
So, what you want seems to be the second part of the inspect-string, 0.1E2 in your case, equal to 10. The comment above is quite clear, this should be the full numeric value of the object. Simple regex will be enough.
another option:
"#<BigDecimal:95915c4,'0.1E2',9(27)>".split(",")[1].tr! "'", ''
=> "0.1E2"
Given I have 2 arrays that contain objects. On those objects there is an datetime attribute and a fixed number of metrics. What I need to do is merge the two arrays and sum the metrics by the datetime.
Array 1:
#<ReadingsProvider::Reading:0x007fc52dd5a0a8 #start_date=2012-12-09 03:00:00 UTC, #item1=13, #item2=46, #item3=6, #item4=4, #item5=9, #item6=17, #item7=34>,
#<ReadingsProvider::Reading:0x007fc52dbd54d0 #start_date=2012-12-09 04:00:00 UTC, #item1=43, #item2=90, #item3=7, #item4=4, #item5=9, #item6=17, #item7=34>,
Array 2
#<ReadingsProvider::Reading:0x007fc52db95f88 #start_date=2012-12-09 03:00:00 UTC, #item1=23, #item2=16, #item3=5, #item4=6, #item5=8, #item6=20, #item7=36>,
#<ReadingsProvider::Reading:0x007fc52db591f0 #start_date=2012-12-09 04:00:00 UTC, #item1=76, #item2=12, #item3=6, #item4=5, #item5=10, #item6=17, #item7=33>
Final Array (assuming my by hand math is right...)
#<ReadingsProvider::Reading:0x007fc52db95f88 #start_date=2012-12-09 03:00:00 UTC, #item1=36, #item2=62, #item3=11, #item4=10, #item5=17, #item6=37, #item7=70>,
#<ReadingsProvider::Reading:0x007fc52db591f0 #start_date=2012-12-09 04:00:00 UTC, #item1=119, #item2=102, #item3=13, #item4=9, #item5=19, #item6=34, #item7=67>
What I ended up doing... needs testing to make sure it's working as expected.
def merge_arrays(array1, array2)
result = array1.concat(array2).group_by(&:start_date).map do |date, array|
my_hash = {
start_date: date,
item1: array.map(&:item1).inject(:+),
item2: array.map(&:item2).inject(:+),
item3: array.map(&:item3).inject(:+),
item4: array.map(&:item4).inject(:+),
item5: array.map(&:item5).inject(:+),
item6: array.map(&:item6).inject(:+),
item7: array.map(&:item7).inject(:+)
}
ReadingsProvider::Reading.new(my_hash)
end
result
end
Specific task, but I try to write some code.
At first, include method to ReadingsProvider::Reading
def concat_with(another)
if #reading_start_date == another.instance_variable_get(:#reading_start_date)
instance_variables.each do |var|
if var.to_s.include?("item") && another_value = another.instance_variable_get(var)
another.instance_variable_set(var, another_value + instance_variable_get(var))
end
end
end
end
Then general approach:
array1.map do |reading|
array2.map do |another|
reading.concat_with(another)
end
end.flatten.compact
If i'd know more, the code should be more elegant anyway. This code looks ugly.
I am using the Twitter Gem to access the Twitter API and I'd like to create a variable that only stores mentions that are unique, based on the text of the mention.
Right now, I'm storing all mentions like so: #allmentions = Twitter.mentions_timeline
This is an example of a mention returned for #allmentions[0]
=> #<Twitter::Tweet:0x007fbffb59ab88 #attrs={:created_at=>"Mon Dec 10 01:28:11 +0000 2012", :id=>277947788216639488, :id_str=>"277947788216639488", :text=>"#person hi", :source=>"web", :truncated=>false, :in_reply_to_status_id=>nil, :in_reply_to_status_id_str=>nil, :in_reply_to_user_id=>11739102, :in_reply_to_user_id_str=>"11739102", :in_reply_to_screen_name=>"person", :user=>{:id=>1000628702, :id_str=>"1000628702", :name=>"test account", :screen_name=>"testaccountso", :location=>"", :description=>"", :url=>nil, :entities=>{:description=>{:urls=>[]}}, :protected=>false, :followers_count=>0, :friends_count=>0, :listed_count=>0, :created_at=>"Mon Dec 10 01:27:39 +0000 2012", :favourites_count=>0, :utc_offset=>nil, :time_zone=>nil, :geo_enabled=>false, :verified=>false, :statuses_count=>1, :lang=>"en", :contributors_enabled=>false, :is_translator=>false, :profile_background_color=>"C0DEED", :profile_background_image_url=>"http://a0.twimg.com/images/themes/theme1/bg.png", :profile_background_image_url_https=>"https://si0.twimg.com/images/themes/theme1/bg.png", :profile_background_tile=>false, :profile_image_url=>"http://a0.twimg.com/sticky/default_profile_images/default_profile_3_normal.png", :profile_image_url_https=>"https://si0.twimg.com/sticky/default_profile_images/default_profile_3_normal.png", :profile_link_color=>"0084B4", :profile_sidebar_border_color=>"C0DEED", :profile_sidebar_fill_color=>"DDEEF6", :profile_text_color=>"333333", :profile_use_background_image=>true, :default_profile=>true, :default_profile_image=>true, :following=>nil, :follow_request_sent=>false, :notifications=>nil}, :geo=>nil, :coordinates=>nil, :place=>nil, :contributors=>nil, :retweet_count=>0, :entities=>{:hashtags=>[], :urls=>[], :user_mentions=>[{:screen_name=>"person", :name=>"Person", :id=>1173910, :id_str=>"1173910", :indices=>[0, 6]}]}, :favorited=>false, :retweeted=>false}>
I can access the text of the mention like so: #allmentions[0].text
Is there a built-in ruby method (or an easy way) to let me store only the mentions that have a unique value in the text attribute?
Yes, you can call uniq with a block.
For example:
#allmentions.uniq {|m| m.text}
To answer my own question, I did a bit of research, and it seems like this would work:
no_dupes = $allmentions.uniq { |h| h[:text] }
I have a calendar screen where I want to display the hours of the day like this:
12:00am
1:00am
2:00am
..
4:00pm
5:00pm
etc.
Being a total Ruby noob, I was wondering if anyone could help me figure out the simplest way to display this.
#!/usr/bin/env ruby
# without using actual `Date` objects ...
p ["12:00am"] + (1..11).map {|h| "#{h}:00am"}.to_a +
["12:00pm"] + (1..11).map {|h| "#{h}:00pm"}.to_a
["12:00am", "1:00am", "2:00am", "3:00am", "4:00am", "5:00am", "6:00am",
"7:00am", "8:00am", "9:00am", "10:00am", "11:00am", "12:00pm", "1:00pm",
"2:00pm", "3:00pm", "4:00pm", "5:00pm", "6:00pm", "7:00pm", "8:00pm",
"9:00pm", "10:00pm", "11:00pm"]
Or using actual DateTime objects and %I:%M%p as format:
#!/usr/bin/env ruby
require "Date"
for hour in 0..23 do
d = DateTime.new(2010, 1, 1, hour, 0, 0)
p d.strftime("%I:%M%p")
end
Which would print:
"12:00AM"
"01:00AM"
"02:00AM"
"03:00AM"
"04:00AM"
"05:00AM"
"06:00AM"
"07:00AM"
"08:00AM"
"09:00AM"
"10:00AM"
"11:00AM"
"12:00PM"
"01:00PM"
"02:00PM"
"03:00PM"
"04:00PM"
"05:00PM"
"06:00PM"
"07:00PM"
"08:00PM"
"09:00PM"
"10:00PM"
"11:00PM"
You could generate these like this:
array = ['12:00am'] + (1..11).map {|h| "#{h}:00am"} + ['12:00pm'] + (1..11).map {|h| "#{h}:00pm"}
or simply write out the array (this is more efficient):
array = ["12:00am", "1:00am", "2:00am", "3:00am", "4:00am", "5:00am", "6:00am", "7:00am", "8:00am", "9:00am", "10:00am", "11:00am", "12:00pm", "1:00pm", "2:00pm", "3:00pm", "4:00pm", "5:00pm", "6:00pm", "7:00pm", "8:00pm", "9:00pm", "10:00pm", "11:00pm"]
You can then print these however you want, eg.
array.each do |el|
puts el
end