lives_a
lives_a

Reputation: 15

converting a hash string into a date object ruby

I created a hash out of file that contains date as a string in different formats (like September 1988, the other line would be July 11th 1960, and sometimes year only)

require 'date'

def create_book_hash(book_array)
  {
    link: book_array[0],
    title: book_array[1],
    author: book_array[2],
    pages: book_array[3].to_i,
    date: book_array[4],
    rating: book_array[5].to_f,
    genre: book_array[6]
  }
end

def books_sorted_by_date (books_array)
    books_array.sort_by { |key| Date.strptime(key[:date], '%Y, %m') }
  end

book_file= File.read("books.txt")
    .split("\n")
    .map { |line| line.split("|")}
    .map { |book_array| create_book_hash(book_array)}
puts books_sorted_by_date(book_file)

I'm trying to sort books by date, so it would be in ascending order by year and since I have different string types, i put a hash key as the first argument in strptime to access all the values in :date . And that gives me \strptime': invalid date (Date::Error).` I don't understand why and what can I do to convert these strings into date objects? (just ruby, no rails)

Upvotes: 0

Views: 344

Answers (2)

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84423

Handle Both Standard and Custom Date Strings

Date#parse doesn't handle arbitrary strings in all cases. Even when it does, it may not handle them the way you expect. For example:

parse_date "1/1/18"
#=> #<Date: 2001-01-18 ((2451928j,0s,0n),+0s,2299161j)>

While Date#parse handles many date formats automagically, it only successfully parses objects that match its internal expectations. When you have multiple or arbitrary date formats, you have to define your own date specifications using Date#strptime to handle those formats that Date#parse doesn't understand, or that it handles incorrectly. For example:

require 'date'

def parse_date str
  Date.parse str
rescue Date::Error
  case str
  when /\A\d{4}\z/
    Date.strptime str, '%Y'
  when /\A\d{2}\z/
    Date.strptime str, '%y'
  else
    raise "unexpected date format: #{str}"
  end
end

date_samples = ["July 11th 1960", "September 1988", "1776"]

date_samples.map { |date| parse_date(date) }
#=> [#<Date: 1960-07-11 ((2437127j,0s,0n),+0s,2299161j)>, #<Date: 1988-09-01 ((2447406j,0s,0n),+0s,2299161j)>, #<Date: 1776-01-01 ((2369731j,0s,0n),+0s,2299161j)>]

This obviously is not an exhaustive list of potential formats, but you can add more examples to date_samples and update the case statement to include any unambiguous date formats you expect from your data set.

Upvotes: 3

Shekhar
Shekhar

Reputation: 54

Date.strptime needs two parameters date-string and format of the date. To use strptime you need to know what is the format of the string beforehand.

see some examples here - https://apidock.com/ruby/Date/strptime/class

In your program you don't know exact format of the date on that line when it parses so you need to try something like -

def books_sorted_by_date (books_array)
    books_array.sort_by { |key| Date.parse(key[:date]) }
  end

Date.parse needs one argument - date string, it then tries to guess the date. see details - https://apidock.com/ruby/v2_6_3/Date/parse/class

You will still have problems with just year with this approach.

Upvotes: 0

Related Questions