If I try to convert single String to date it works.
require 'date'
a=String.new
a='20171023'
puts b=Date.strptime(a,'%Y%m%d')
puts b.yday()
How can I make it work with an array? I tried this way.
require 'date'
a=[20160106, 20132018, 20011221]
b=a.each{|a| Date.strptime(a, '%Y%m%d').yday()}
puts b
You need to pass a string, instead an integer as you're doing now:
a = ['20160106', '20130218', '20011221']
If you want to store the result of each operation in b, then you can use map instead each:
b = a.map { |date| Date.strptime(date, '%Y%m%d') }
Your second date is invalid, I guess is 20130218.
require 'date'
a = %w[20160106 20130218 20011221]
b = a.map { |date| Date.strptime(date, '%Y%m%d').yday }
p b # [6, 49, 355]
%w[ ... ] is an array of strings, where you avoid using quotes and commas.
When you don't need to pass arguments to a method call, you can avoid parenthesis.
require 'date'
a = [20160106, 20131018, 20011221]
a.map { |n| (Date.parse n.to_s).yday }
NB the array is different from the OP's, I assume he made a typo of some sort as the second number-date was invalid.
Related
a = ["SUPER", "SOME_VALID", "ROME_INVALID", "SUPER_GOOD"]
a = a.reject { |x| x.in? ["GOOD", "VALID"]}
#=> ["SUPER", "SOME_VALID", "ROME_INVALID", "SUPER_GOOD"]
I don't want words that contain substring VALID or GOOD.
Output should be ["SUPER"] only.
grep_v would work:
a = ["SUPER", "SOME_VALID", "ROME_INVALID", "SUPER_GOOD"]
a = a.grep_v(/GOOD|VALID/)
#=> ["SUPER"]
You could say this:
a = a.reject { |x| x.include?("GOOD") || x.include?("VALID")}
What in? does is to check if the receiver is present in the array passed as argument, meaning:
1.in?([1, 2]) # true
3.in?([1, 2]) # false
That's to say, it checks for the "whole" object, rather than a part of it.
If you want to reject the elements in your array that match with VALID and/or GOOD, you can use =~:
["SUPER", "SOME_VALID", "ROME_INVALID", "SUPER_GOOD"].reject { |word| word =~ /VALID|GOOD/ } # ["SUPER"]
Notice, this is also going to reject words like "VALIDITY", "GOODNESS", etc.
You could use include?:
a.reject { |x| ["GOOD", "VALID"].any?{ |word| x.include?(word) } }
#=> ["SUPER"]
a little help with getting data out of a string.
Assuming I executed a sql query and now have a string(which set as hash on db):
"{\"users_associated\":{\"User:4\":6,\"User:22\":28,\"User:30\":36}}"
(Which stands for User:ID : User.display_id)
How can I get a substring the includes all users ids or all their display ids, so I'll have something like 4,22,30 or 6,22,36)?
Thanks!
It's common for data systems to return data in a serialized form, i.e. using data types that facilitate transmission of data. One of these serializable data types is String, which is how your JSON data object has been received.
The first step would be to de-serialize (or parse) this String into a Hash object using JSON.parse and tease out just the data value for key "users_associated".
your_string = "{\"users_associated\":{\"User:4\":6,\"User:22\":28,\"User:30\":36}}"
hash = JSON.parse(your_string)
data = hash["users_associated"]
#=> {"User:4":6, "User:22": 28, "User:30": 36}
Hash#keys gives you an array of a hash's keys.
Hash#values gives you an array of a hash's data values.
keys = data.keys
#=> ["User:4", "User:22", "User:30"]
values = data.values
#=> [6, 28, 36]
Array#join lets you string together the contents of an array with a defined separator, , in this case.
display_ids = keys.join(',')
#=> "6,28,36"
For the User IDs, you could Array#map every element of the values array to replace every string occurrence of "User:" with "", using String#gsub.
user_ids = values.map{|user_id| user_id.gsub("User:", "")}
#=> ["4", "22", "30"]
Then, in a similar way to display_ids, we can Array#join the contents of the user_ids array to a single string.
user_ids = user_ids.join(",")
#=> "4,22,30"
You can create two helper methods. I'm leaving return values as arrays because I assume you would need to iterate on them at some point and also converting the user id's to integers.
def extract_display_ids(json)
json['users_associated'].values
end
def extract_user_ids(some_data)
json['users_associated'].keys.map{ |key| key.split(':').last.to_i }
end
some_data = JSON.parse("{\"users_associated\":{\"User:4\":6,\"User:22\":28,\"User:30\":36}}")
extract_display_ids(some_data)
#=> [6, 28, 36]
extract_user_ids(some_data)
#=> [4, 22, 30]
If possible though, I would recommend trying to get a better data format:
{ users_associated:
[{ user_id : 4, display_id:6 }, { user_id : 4, display_id:6 }]
}
I wrote class for this. If you want, you can add it to your project and use it as follows:
require 'json'
class UserSubstringExtractor
def initialize(user_json_data)
#user_json_data = user_json_data
end
def display_ids
user_data.dig('users_associated').values
end
def user_ids
user_data.dig('users_associated').keys.map { |u| u.split(':').last.to_i }
end
private
def user_data
JSON.parse(#user_json_data)
end
end
user_json_data = '{"users_associated":{"User:4":6,"User:22":28,"User:30":36}}'
extractor = UserSubstringExtractor.new(user_json_data)
p extractor.display_ids
#=> [6, 28, 36]
p extractor.user_ids
#=> [4, 22, 30]
I've got some issue with DateTime in Ruby
I've got line which looks like this (it's in .txt file)
DateTime.new(1979,1,1) DateTime.new(2012,3,29)
And my function to get this looks like this
def split_line
array = line.split(' ')
#date_of_birth = array[0]
#date_of_death = array[1]
end
But #date_of_birth and #date_of_death class are String. How can I get them as DateTime?
Assuming your string is in the correct format, then you're probably looking for:
#date_of_birth = array[0].to_datetime
#date_of_death = array[1].to_datetime
See here for more info:
https://apidock.com/rails/String/to_datetime
This:
DateTime.new(1979,1,1) DateTime.new(2012,3,29)
Is not code. What do you expect that to do?
If you want two DateTimes as a space-separated string, do something like:
"#{DateTime.new(1979,1,1)} #{DateTime.new(2012,3,29)}"
When you have something like #{...} inside a set of double quotation marks (they must be double, not single quotation marks), it's called string interpolation. Learn it. Love it. Live it.
But, for the life of me, I don't know why you wouldn't do:
[DateTime.new(1979,1,1), DateTime.new(2012,3,29)]
Which gives you an array, so no split needed. Just:
def split_line
#date_of_birth = array[0]
#date_of_death = array[1]
end
If you want DateTime values, grab the numbers and create them:
require 'date'
'DateTime.new(1979,1,1) DateTime.new(2012,3,29)'.split.map { |s|
DateTime.new(*s.scan(/\d+/).map(&:to_i) )
}
# => [#<DateTime: 1979-01-01T00:00:00+00:00 ((2443875j,0s,0n),+0s,2299161j)>,
# #<DateTime: 2012-03-29T00:00:00+00:00 ((2456016j,0s,0n),+0s,2299161j)>]
The values aren't DateTime though, they're Dates:
'DateTime.new(1979,1,1) DateTime.new(2012,3,29)'.split.map { |s|
Date.new(*s.scan(/\d+/).map(&:to_i) )
}
# => [#<Date: 1979-01-01 ((2443875j,0s,0n),+0s,2299161j)>,
# #<Date: 2012-03-29 ((2456016j,0s,0n),+0s,2299161j)>]
Breaking it down:
'DateTime.new(1979,1,1) DateTime.new(2012,3,29)'.split # => ["DateTime.new(1979,1,1)", "DateTime.new(2012,3,29)"]
.map { |s|
Date.new(
*s.scan(/\d+/) # => ["1979", "1", "1"], ["2012", "3", "29"]
.map(&:to_i) # => [1979, 1, 1], [2012, 3, 29]
)
}
# => [#<Date: 1979-01-01 ((2443875j,0s,0n),+0s,2299161j)>,
# #<Date: 2012-03-29 ((2456016j,0s,0n),+0s,2299161j)>]
* (AKA "splat"), used like this, explodes an array into its elements, which is useful when you have an array but the method only takes separate parameters.
The bigger question is why you're getting values like that in a text file.
I have a CSV file with two columns:
PPS_Id Amount
123 100
1234 150
I read data from this file and insert in a array using the code below:
CSV.foreach("filename.CSV", headers: true) do |row|
file_details << row.inspect # hash
end
I am then trying to push the data in the file_details into a hash with PPS_Id as key and Amount as Value, I am using the code below:
file_details_hash = Hash.new
file_details.each { |x|
file_details_hash[x['PPS_Id']] = x['Amount']
}
But when I print the result I get nothing just {"PPS_Id"=>"Amount"}
Can you please help
Your code, modified to work
You need to specify the column separator for your csv, and remove inspect.
require 'csv'
file_details = []
CSV.foreach("filename.CSV", headers: true, col_sep: "\s" ) do |row|
file_details << row
end
file_details_hash = Hash.new
file_details.each { |x|
file_details_hash[x['PPS_Id']] = x['Amount']
}
p file_details_hash
#=> {"123"=>"100", "1234"=>"150"}
It now returns what you expected to get.
Shorter solution
Read the csv, drop the first line (header) and convert to a Hash :
p CSV.read("filename.CSV", col_sep: "\s").drop(1).to_h
#=> {"123"=>"100", "1234"=>"150"}
First of all, you are collecting strings into an array (see String#inspect):
file_details << row.inspect
After that you call (sic!) String#[] on that strings:
x['PPS_Id'] #⇒ "PPS_Id", because string contains this substring
That said, your code has nothing but errors. You might achieve what you want with:
csv = CSV.parse(File.read("filename.CSV"), col_sep: "\s")
csv[1..-1].to_h
#⇒ {
# "123" => "100",
# "1234" => "150"
# }
Using inspect will save your CSV rows as strings, so obviously you won't be able get what you need. Instead try this:
file_details = CSV.read("filename.csv")
Read CSV directly will create an 2D array that you can then iterate over, which will look like this: [["PPS_Id", "Amount"], ["123", "100"], ["1234", "150"]]
From there you can slightly modify your approach:
file_details.each do |key, value|
file_details_hash[key] = value
end
To receive a hash like this: {"PPS_Id"=>"Amount", "123"=>"100", "1234"=>"150"}
Somehow, the webservice I am using savon to deal with returns this response
{:do_deal_response=>{:do_deal_result=>"Code=000&CardNumber=0000&CardDate=12/18&PayDate=02/01/2017&BusinessName=בדיקות&Terminal=0962317010&ActionMethod=עסקה טלפונית&DealID=5349614&DealType=אשראי רגיל&DealTypeOut=עסקת חובה רגילה 0000000&OkNumber=0000000&DealDate=06/12/2016 11:00:14&PayNumber=&TotalSum=111&FirstPay=&CardName=ויזה כ.א.ל&CardNameID=2&AuthNum=0000000&DealNumber=06370507&ParamJ=J4&ErrorDesc=עסקה תקינה.&Currency=שקלים&CurrencyID=1&Manpik=ויזה כ.א.ל&ManpikID=2&Mutag=ויזה כ.א.ל&MutagID=2", :#xmlns=>"http://tempuri.org/"}}
accessing #response[:do_deal_response][:do_deal_result] obviously returns the string
"Code=000&CardNumber=0000&CardDate=12/18&PayDate=02/01/2017&BusinessName=בדיקות&Terminal=0962317010&ActionMethod=עסקה טלפונית&DealID=5349614&DealType=אשראי רגיל&DealTypeOut=עסקת חובה רגילה 0000000&OkNumber=0000000&DealDate=06/12/2016 11:00:14&PayNumber=&TotalSum=111&FirstPay=&CardName=ויזה כ.א.ל&CardNameID=2&AuthNum=0000000&DealNumber=06370507&ParamJ=J4&ErrorDesc=עסקה תקינה.&Currency=שקלים&CurrencyID=1&Manpik=ויזה כ.א.ל&ManpikID=2&Mutag=ויזה כ.א.ל&MutagID=2"
I want to be able to access this string as a hash with with symbols
like:
results[:code] = "000"
Instead of manually parsing, you can use the following:
require 'cgi'
parsed = CGI.parse(#response[:do_deal_response][:do_deal_result])
parsed['Code']
#=> ["000"]
Since this accepts multiple values for each key values are always wrapped in an array. If that bothers you, you can do one more transformation step with map (or map_values if you use Rails):
parsed.map { |k, v| [k.underscore.symbolize, v.size == 1 ? v.first : v] }.to_h
#=> => {:code=>"000", :card_number=>"0000", :ard_date=>"12/18", :pay_date=>"02/01/2017",...
This will only unwrap arrays with one element, everything else will stay in an array.
Stand-alone solution
This code could help you. It just splits the string around &, split every part around = and saves them as key and value in a Hash :
response = {:do_deal_response=>{:do_deal_result=>"Code=000&CardNumber=0000&CardDate=12/18&PayDate=02/01/2017&BusinessName=בדיקות&Terminal=0962317010&ActionMethod=עסקה טלפונית&DealID=5349614&DealType=אשראי רגיל&DealTypeOut=עסקת חובה רגילה 0000000&OkNumber=0000000&DealDate=06/12/2016 11:00:14&PayNumber=&TotalSum=111&FirstPay=&CardName=ויזה כ.א.ל&CardNameID=2&AuthNum=0000000&DealNumber=06370507&ParamJ=J4&ErrorDesc=עסקה תקינה.&Currency=שקלים&CurrencyID=1&Manpik=ויזה כ.א.ל&ManpikID=2&Mutag=ויזה כ.א.ל&MutagID=2", :#xmlns=>"http://tempuri.org/"}}
deal_result = response[:do_deal_response][:do_deal_result]
class String
def underscore
self.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
end
results = deal_result.split('&').each_with_object({}) do |string, h|
key, value = string.split('=')
h[key.underscore.to_sym] = value
end
puts results
#=> {:code=>"000", :card_number=>"0000", :card_date=>"12/18", :pay_date=>"02/01/2017", :business_name=>"בדיקות", :terminal=>"0962317010", :action_method=>"עסקה טלפונית", :deal_id=>"5349614", :deal_type=>"אשראי רגיל", :deal_type_out=>"עסקת חובה רגילה 0000000", :ok_number=>"0000000", :deal_date=>"06/12/2016 11:00:14", :pay_number=>nil, :total_sum=>"111", :first_pay=>nil, :card_name=>"ויזה כ.א.ל", :card_name_id=>"2", :auth_num=>"0000000", :deal_number=>"06370507", :param_j=>"J4", :error_desc=>"עסקה תקינה.", :currency=>"שקלים", :currency_id=>"1", :manpik=>"ויזה כ.א.ל", :manpik_id=>"2", :mutag=>"ויזה כ.א.ל", :mutag_id=>"2"}
With Rack
If you have Rack installed :
require 'rack'
puts Rack::Utils.parse_nested_query(deal_result).map { |k, v| [k.underscore.to_sym, v] }.to_h
Both answers match exactly the structure you were looking for : snakecase symbol for key and non Array values.