I have a 2d array that contains some nil values.
require 'date'
array = [["2014-01-12", "2014-01-12", "2014-01-12"],
["2012-08-26", "2012-10-18", nil],
["2013-04-09", "2013-05-22", "2013-07-01"]]
The desired result is an array of date objects. The resulting array should look like this
(Date objects for display purposes):
changed_array = [#<Date: 2014-01-12 ((2456874j,0s,0n),+0s,2299161j)>, nil, #<Date: 2012-07-31 ((2456874j,0s,0n),+0s,2299161j)>]
I considered something like:
changed_array = array.map { |due| (Date.strptime(due[2], "%Y-%m-%d")) unless (due[2] == nil) }
Any input is appreciated.
EDIT:
As a newbie to coding I would appreciate any input on alternative approaches to this solution!
Similar to Uri's solution, but with only one map
array.map {|*_, d| Date.parse(d) if d}
# => [#<Date: 2014-01-12 ((2456670j,0s,0n),+0s,2299161j)>, nil, #<Date: 2013-07-01 ((2456475j,0s,0n),+0s,2299161j)>]
If you want the last element to be parsed into date you can do the following:
changed_date = array.map(&:last).map { |d| Date.parse(d) if d }
# => [#<Date: 2014-01-12 ((2456670j,0s,0n),+0s,2299161j)>, nil, #<Date: 2013-07-01 ((2456475j,0s,0n),+0s,2299161j)>]
The first map takes only the last element of each array, and the second parses the date, unless it is nil.
Related
Is there a straightforward way to do something like the following without excessive looping?
myArray = [["a","b"],["c","d"],["e","f"]]
if myArray.includes?("c")
...
I know this works fine if it's just a normal array of chars... but I would like something equally as elegant for an array of an array of chars (bonus points for helping convert this to an array of tuples).
If you only need a true/false answer you can flatten the array and call include on that:
>> myArray.flatten.include?("c")
=> true
You can use assoc:
my_array = [['a', 'b'], ['c', 'd'], ['e', 'f']]
if my_array.assoc('c')
# ...
It actually returns the whole subarray:
my_array.assoc('c') #=> ["c", "d"]
or nil if there is no match:
my_array.assoc('g') #=> nil
There's also rassoc to search for the second element:
my_array.rassoc('d') #=> ["c", "d"]
my_array = [["a","b"],["c","d"],["e","f"]]
p my_hash = my_array.to_h # => {"a"=>"b", "c"=>"d", "e"=>"f"}
p my_hash.key?("c") # => true
You can use Array#any?
myArray = [["a","b"],["c","d"],["e","f"]]
if myArray.any? { |x| x.includes?("c") }
# some code here
The find_index method works well for this:
myArray = [["a","b"],["c","d"],["e","f"]]
puts "found!" if myArray.find_index {|a| a[0] == "c" }
The return value is the array index of the pair or nil if the pair is not found.
You can capture the pair's value (or nil if not found) this way:
myArray.find_index {|a| a[0] == "c" } || [nil, nil])[1]
# => "d"
How to remove the tailing nil/empty values in array?
I want to remove the tailing nil values in the following array,
So the array size may become 125, not 127
...
[123] "Conc_Net_LE_8_TDR_Long_Other",
[124] "Conc_Net_LE_8_TDR_Short_Other",
[125] "Contract_Units",
[126] nil,
[127] nil,
["foo", nil, ""].grep(/./)
# => ["foo"]
You can do
new_array = array.compact.delete("")
#compact will remove all nil objects, and using #delete, you can delete all empty string object(""). You can also do :
array.delete_if { |elem| elem.nil? || elem.empty? }
Use Array#compact, or the bang version compact! to modify the called object to remove the nil elements.
> arr = [1, nil]
> arr.compact
=> [1]
To remove nil and empty you can use Array#reject or also the bang version reject!
arr = [1, nil, ""]
arr.reject { |i| i.to_s.empty? }
=> [1]
If you are interested to remove only the tailing nil and empty sequence, maybe you can do something like this
array = ["1","2","3",nil," ","4",nil,nil, ""]
rev = array.reverse
while rev[0] == nil or is_empty_sequence?(rev[0])
rev.shift
end
p rev.reverse #Output -> ["1","2","3",nil," ","4"]
where is_empty_sequence? is a method where you declared what are the empty sequence.
I have a two-dimensional array of a bunch of strings, including some empty strings. I want to replace the empty strings with nil. How do I do this in Ruby?
Sample array:
[['cat','','dog',''],['','','fish','','horse']]
Desired output:
[['cat',nil,'dog',nil],[nil,nil,'fish',nil,'horse']]
[['cat','','dog',''],['','','fish','','horse']].map do |arr|
arr.map { |s| s unless s.empty? }
end
# => [["cat", nil, "dog", nil], [nil, nil, "fish", nil, "horse"]]
Is it possible to convert quickly a strftime("%u") value to a strftime("%A") or do i need to build an equivalence hash like {"Monday" => 1, ......... "Sunday" => 6}
I have an Array with some day as decimal values
class_index=[2,6,7]
and I would like to loop through this array to build and array of days name like this
[nil, "Tuesday", nil, nil, nil, "Saturday", "Sunday"]
so I could do
class_list=[]
class_index.each do |x|
class_list[x-1] = convert x value to day name
end
Is that even possible?
How about:
require "date"
DateTime.parse("Wednesday").wday # => 3
Oh, I now see you've expanded your question. How about:
[2,6,7].inject(Array.new(7)) { |memo,obj| memo[obj-1] = Date::DAYNAMES[obj%7]; memo }
Let me explain that one:
input = [2,6,7]
empty_array = Array.new(7) # => [nil, nil, nil, nil, nil, nil, nil]
input.inject(empty_array) do |memo, obj| # loop through the input, and
# use the empty array as a 'memo'
day_name = Date::DAYNAMES[obj%7] # get the day's name, modulo 7 (Sunday = 0)
memo[obj-1] = day_name # save the day name in the empty array
memo # return the memo for the next iteration
end
The beauty of Ruby.
To go from decimal to weekday:
require 'date'
Date::DAYNAMES[1]
# => "Monday"
So, in your example, you could simply do:
class_list=[]
class_index.each do |x|
class_list[x-1] = Date::DAYNAMES[x-1]
end
Here’s one way that comes to mind:
require "date"
def weekday_index_to_name(index)
date = Date.parse("2011-09-26") # Canonical Monday.
(index - 1).times { date = date.succ }
date.strftime("%A")
end
class_index=[2,6,7]
class_index.map{|day_num| Date::DAYNAMES[day_num%7]}
#=> ["Tuesday", "Saturday", "Sunday"]
note that day names are from 0 to 6, so you can either work from 0 to 6 or have it modulo 7
Let's say I had a Ruby Array of Dates like:
2011-01-20
2011-01-23
2011-02-01
2011-02-15
2011-03-21
What would be an easy and Ruby-esque way of creating a Hash that groups the Date elements by year and then month, like:
{
2011 => {
1 => [2011-01-20, 2011-01-23],
2 => [2011-02-01, 2011-02-15],
3 => [2011-03-21],
}
}
I can do this by iterating over everything and extracting years, months and so on, then comining them.
Ruby offers so many methods and blocks for Arrays and Hashes, there must be an easier way?
require 'date'
dates = [
'2011-01-20',
'2011-01-23',
'2011-02-01',
'2011-02-15',
'2011-03-21'
].map{|sd| Date.parse(sd)}
Hash[
dates.group_by(&:year).map{|y, items|
[y, items.group_by{|d| d.strftime('%B')}]
}
]
#=> {2011=>{"January"=>[#<Date: 2011-01-20 (4911163/2,0,2299161)>, #<Date: 2011-01-23 (4911169/2,0,2299161)>], "February"=>[#<Date: 2011-02-01 (4911187/2,0,2299161)>, #<Date: 2011-02-15 (4911215/2,0,2299161)>], "March"=>[#<Date: 2011-03-21 (4911283/2,0,2299161)>]}}
I noticed you have changed month names into numbers, so you may want to replace d.strftime('%B') above with d.month or whatever else.
Here's a step-by-step explanation:
You essentially want two-level grouping: first level by year, second by month. Ruby has very useful method group_by, which groups elements by given expression (a block). So: first part is grouping original array by year:
hash_by_year = dates.group_by(&:year)
# => {2011=>[#<Date: 2011-01-20 (4911163/2,0,2299161)>, #<Date: 2011-01-23 (4911169/2,0,2299161)>, #<Date: 2011-02-01 (4911187/2,0,2299161)>, #<Date: 2011-02-15 (4911215/2,0,2299161)>, #<Date: 2011-03-21 (4911283/2,0,2299161)>]}
That gives us first level: keys are years, values arrays of dates with given year. But we still need to group the second level: that's why we map by-year hash - to group its values by month. Let's for start forget strftime and say that we're grouping by d.month:
hash_by_year.map{|year, dates_in_year|
[year, dates_in_year.group_by(&:month)]
}
# => [[2011, {1=>[#<Date: 2011-01-20 (4911163/2,0,2299161)>, #<Date: 2011-01-23 (4911169/2,0,2299161)>], 2=>[#<Date: 2011-02-01 (4911187/2,0,2299161)>, #<Date: 2011-02-15 (4911215/2,0,2299161)>], 3=>[#<Date: 2011-03-21 (4911283/2,0,2299161)>]}]]
That way we got our second level grouping. Instead of array of all dates in a year, now we have hash whose keys are months, and values arrays of dates for a given month.
The only problem we have is that map returns an array and not a hash. Thats why we "surround" whole expression by Hash[], which makes a hash out of array of pairs, in our case pairs [year, hash_of_dates_by_month].
Sorry if the explanation sounds confusing, I found harder to explain functional expressions than imperative, because of the nesting. :(
This gets you pretty close, you just need to change the numerical month number into a textual month name:
dates = %w(
2011-01-20
2011-01-23
2011-02-01
2011-02-15
2011-03-21
)
grouped = dates.inject({}) do |ret, date|
y,m,d = date.split('-')
ret[y] ||= {}
# Change 'm' into month name here
ret[y][m] ||= []
ret[y][m] << date
ret
end
puts grouped.inspect
dates = %w(
2011-01-20
2011-01-23
2011-02-01
2011-02-15
2011-03-21
)
hash = {}
dates.each do |date|
year, month = date.strftime('%Y,%B').split(',')
hash[year] ||= {}
hash[year][month] = hash[year][month].to_a << date
end