How to change values in an array in an efficient way - ruby-on-rails

I am getting the following arrays from external api endpoint.
Input:-
1. [["date", "country_name", "month"], ["2019-02-21", "US", "Jan"]]
2. ["name", "homeAddress", "zipcode"]
Expected Output:-
1. [["Date", "Country Name", "Month"], ["2019-02-21", "US", "Jan"]]
2. ["Name", "Home Address", "Zipcode"]
How can I change the each array in an efficient way in Ruby on Rails?
Update:
Some of the name are different in expected as follows
Input:
["column1", "column2", "date"]
Expected output:
["column3", "column4", "Date"]
How can I get the above output?
Answer:-
Inputs:-
a=['1', '2', '3', '4']
b= {"1"=>"10", "2"=>"20", "3"=>"30"}
Execute:
c=a.map{|i| b[i].nil?? i : b[i] }
Output:-
["10", "20", "30", "4"]

You want to replace '_' with space or bring space when capital letter is encountered within string,
Try following rails methods to do so,
"now_isTheTime".titleize.camelize
=> "Now Is The Time"

ar1 = [["date", "country_name", "month"], ["2019-02-21", "US", "Jan"]]
ar2 = ["name", "homeAddress", "zipcode"]
def formatter(string)
return string if string.length < 3 || string.count("0-9").positive?
string.titleize.camelize
end
ar1.map{ |sub_arr| sub_arr.map(&method(:formatter)) }
ar2.map(&method(:formatter))

Related

From array to 2D array devided by 2 values

I'm having an array:
arr =["112000666", "10", "111282637", "15", "111342625", "12", "112000674",
"11", "111488203", "18", "111237150", "20"]
Is there any way to make a 2D array and divided by 2 values? Something like this:
[["112000666", "10"], ["111282637", "15"], ["111342625", "12"],
["112000674", "11"], ["111488203", "18"], ["111237150", "20"]]
The number of elements will always be even.
For rails you can use in_groups_of method:
arr.in_groups_of(2)
#=> [["112000666", "10"], ["111282637", "15"], ["111342625", "12"],
# ["112000674", "11"], ["111488203", "18"], ["111237150", "20"]]
Pure Ruby:
arr.each_slice(2).to_a
#=> [["112000666", "10"], ["111282637", "15"], ["111342625", "12"],
# ["112000674", "11"], ["111488203", "18"], ["111237150", "20"]]
See Enumerable#each_slice.

ruby sorting to re-order array elements using another array

I am looking for an optimal way to use one array to sort another. The code below works but poor performance wise because it is O(n^2).
re_ordered_array = []
use_for_ordering = ["title", "creator", 'alternate_identifier', "keyword"]
array_to_reorder = ["keyword_1", "title", "creator_1", "keyword_2", "creator_2", 'alternate_identifier']
use_for_ordering.each do |value|
re_ordered_array << array_to_reorder.select {|r| r =~ /#{value}/}
end
The result of the above will return the new array ordered correctly:
re_ordered_array.flatten.compact
["title", "creator_1", "creator_2", "alternate_identifier", "keyword_1", "keyword_2"]
To improve how re-order/resort the keys I tried the approach below
hash = use_for_ordering.each_with_index.to_h
The hash above prints out
{"title"=>0, "creator"=>1, "alternate_identifier"=>2, "keyword"=>3}
which i then use here when re-sorting below
array_to_reorder.sort.each do |k|
use_for_ordering.any? do |i|
re_ordered_array.insert(hash[i], k) if k =~ /#{i}/
end
end
The outcome is this
re_ordered_array.compact
which gives
["title", "creator_2", "creator_1", "keyword_2", "keyword_1", "alternate_identifier"]
The problem with the new array is that creator_1 should be before creator_2, same thing applies to keyword_1 and keyword_2
but the end result of what with I want is below but without O(n^2) or resort the array multiple time
["title", "creator_1", "creator_2", "alternate_identifier", "keyword_1", "keyword_2"]
updated array with better representative data
representative_array_order = ["id", "date_uploaded", "date_modified", "file_url", "visibility", "embargo_end_date", "visibility_after_embargo", "lease_end_date", "visibility_after_lease", "collection", "work_type", "resource_type", "title", "creator", "contributor", "doi", "alternate_identifier", "version", "related_identifier", "series_name", "volume", "edition", "journal_title", "book_title", "issue", "pagination", "editor", "publisher", "place_of_publication", "isbn", "issn", "eissn", "article_number", "material_media", "date_published", "date_accepted", "date_submitted", "abstract", "keyword", "institution", "organisational_unit", "peer_reviewed", "official_url", "related_url", "related_exhibition", "related_exhibition_date", "project_name", "funder", "funder_project_reference", "additional_information", "license", "rights_statement", "rights_holder", "language", "event_title", "event_date", "event_location"]
representative_array_for_recorder = ["file_1", "id", "title_1", "date_uploaded", "date_modified", "account_cname", "institution_1", "institution_2", "institution_3", "institution_4", "institution_5", "funder_1", "funder_2", "date_published", "date_accepted", "date_submitted", "project_name_1", "project_name_2", "rights_holder_1", "rights_holder_2", "doi", "place_of_publication_1", "place_of_publication_2", "abstract", "alternate_identifier_1", "alternate_identifier_type_1", "alternate_identifier_2", "alternate_identifier_type_2", "alternate_identifier_3", "alternate_identifier_type_3", "related_identifier_type_1", "related_identifier_id_1", "related_identifier_relationship_1", "related_identifier_type_2", "related_identifier_id_2", "related_identifier_relationship_2", "library_of_congress_classification_1", "library_of_congress_classification_2", "alt_title_1", "alt_title_2", "volume_1", "pagination", "issn", "eissn", "edition", "event_title", "event_date", "event_location", "book_title", "journal_title", "issue", "isbn", "related_exhibition", "related_exhibition_date", "version", "alternative_journal_title_1", "alternative_journal_title_2", "resource_type_1", "creator_family_name_1", "creator_name_type_1", "creator_orcid_1", "creator_isni_1", "creator_position_1", "creator_given_name_2", "creator_family_name_2", "creator_name_type_2", "creator_orcid_2", "creator_isni_2", "creator_position_2", "creator_name_type_3", "creator_isni_3", "creator_position_3", "creator_organisation_name_3", "contributor_given_name_1", "contributor_family_name_1", "contributor_name_type_1", "contributor_orcid_1", "contributor_isni_1", "contributor_position_1", "contributor_name_type_2", "contributor_isni_2", "contributor_position_2", "contributor_organisation_name_2", "description", "keyword_1", "keyword_2", "keyword_3", "license_1", "license_2", "rights_statement_1", "publisher_1", "date_created", "subject", "language", "related_url_1", "related_url_2", "source", "embargo_end_date", "lease_end_date", "visibility", "visibility_after_embargo", "work_type", "visibility_after_lease", "collection", "organisation_unit_1", "organisation_unit_2", "peer_reviewed", "funder_project_reference_1", "funder_project_reference_2", "additional_information", "official_url", "article_number", "material_media"]
I can suggest this option:
Setup
array_to_reorder = use_for_ordering.each_with_object({}) { |k, h| h[k] = array_to_reorder.select { |e| e.start_with? k } }
#=> {"title"=>["title"], "creator"=>["creator_1", "creator_2"], "alternate_identifier"=>["alternate_identifier"], "keyword"=>["keyword_1", "keyword_2"]}
use_for_ordering = use_for_ordering.map.zip(0..).to_h
#=> {"title"=>0, "creator"=>1, "alternate_identifier"=>2, "keyword"=>3}
You could sort the values of array_to_reorder before the next step, if required.
Sorting
array_to_reorder.sort_by { |k, _| use_for_ordering[k] }.flat_map(&:last)
#=> ["title", "creator_1", "creator_2", "alternate_identifier", "keyword_1", "keyword_2"]
You probably want to prepare the array of regexps upfront instead of creating them out of strings on each iteration in the first place.
use_for_ordering =
%w[title creator alternate_identifier keyword].
map(&Regexp.method(:new))
#⇒ [/title/, /creator/, /alternate_identifier/, /keyword/]
Or, even better, create the enumerator:
use_for_ordering =
%w[title creator alternate_identifier keyword].
map(&Regexp.method(:new)).each_with_index
#⇒ #<Enumerator: ...>
Now you might group values and then sort by index:
array_to_reorder.
group_by do |e|
key = use_for_ordering.find { |r, i| r =~ e }
key.nil? ? Float::INFINITY : key.last
end.sort.flat_map(&:last)
#⇒ ["title", "creator_1", "creator_2",
# "alternate_identifier", "keyword_1", "keyword_2"]

Iterate hash of arrays

{"title"=>["111", "222", "333"], "rating"=>["1", "2", "3"], "reviews"=>["11", "22", "33"]}
I have data with me from the form in the above format.
Now I want to iterate through this loop so that I can get data in below sequence as per my requirement:
["111", "1", "11"], ["222", "2", "22"], ["333", "3", "33"]
Actually I have to save each record in my database in the way so that a title, a rating and a review forms one row for my database table.
I have tried many possible solutions with 'each_with_index', key, value hash way, but no luck.
Thanks in advance.
Assuming every value in Hash is an Array of same length, transpose array hash.values :
hash = {"title"=>["111", "222", "333"], "rating"=>["1", "2", "3"], "reviews"=>["11", "22", "33"]}
hash.values
#=> [["111", "222", "333"], ["1", "2", "3"], ["11", "22", "33"]]
hash.values.transpose
#=> [["111", "1", "11"], ["222", "2", "22"], ["333", "3", "33"]]
While the transpose solution is very nice, it will error out when not all value arrays have the same number of elements. This will still work (replacing missing elements with nil):
h.values.reduce(:zip).map(&:flatten)
#=> [["111", "1", "11"], ["222", "2", "22"], ["333", "3", "33"]]
If you know that your input is well-formed, go for the transpose option.

Get & store digits from string

I have a string of values like this:
=> "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
I want to put each value in an array so I can use each do. So something like this:
#numbers =>["72", "58", "49", "62", "9", "13", "17", "63"]
This is the code I want to use once the string is a usable array:
#numbers.each do |n|
#answers << Answer.find(n)
end
I have tried using split() but the characters are not balanced on each side of the number. I also was trying to use a regex split(/\D/) but I think I am just getting worse ideas.
The controller:
#scores = []
#each_answer = []
#score.answer_ids.split('/').each do |a|
#each_answer << Answer.find(a).id
end
Where #score.answer_ids is:
=> "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
Looks like an array of JSON strings. You could probably use Ruby's built-in JSON library to parse it, then map the elements of the array to integers:
input = "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
require 'json'
ids = JSON.parse(input).map(&:to_i)
#answers += Answer.find(ids)
I'd use:
foo = "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
foo.scan(/\d+/) # => ["3", "4", "60", "71", "49", "62", "9", "14", "17", "63"]
If you want integers instead of strings:
foo.scan(/\d+/).map(&:to_i) # => [3, 4, 60, 71, 49, 62, 9, 14, 17, 63]
If the data originates inside your system, and isn't the result of user input from the wilds of the Internet, then you can do something simple like:
bar = eval(foo) # => ["3", "4", "60", "71", "49", "62", "9", "14", "17", "63"]
which will execute the contents of the string as if it was Ruby code. You do NOT want to do that if the input came from user input that you haven't scrubbed.
In your code n is a String, not an Integer. The #find method expects an Integer, so you need to convert the String to an Array of Integers before iterating over it. For example:
str = "[\"3\", \"4\", \"60\", \"71\", \"49\", \"62\", \"9\", \"14\", \"17\", \"63\"]"
str.scan(/\d+/).map(&:to_i).each do |n|
#answers << Answer.find(n)
end

Array Compare in Ruby on Rails

I have two arrays, one is two dimensional and another one dimensional as:
array1 = [["San Francisco", 8], ["New York", 3], ["Madison", 2], ["Washington", 3], ["Tulsa", 3]]
array2 = ["Durham", "Rochester", "New York", "Tulsa", "Kenner", "Washington", "Linton", "Kansas City", "San Francisco", "Madison"]
I want to compare the arrays for existence of City Name in both arrays and show total users as given in the first array (second elements) or 0 if the city is not in first array.
The output should be like this:
Durham (0)
Rochester (0)
New York (3)
Tulsa(3)
Kenner (0)
Washington (3)
...
How can I achive this in Rails?
EDIT:
Actually I have tried array1-array2 to get the differences and adding the differences to array1 with second value 0 but this didn't work for me.
Thanks in advance.
array1 is a perfect candidate to be converted to Hash.
h=Hash[array1]
array2.each{|city| puts "%s(%d)" % [city, h[city]||0] }
Using Array#assoc:
array1 = [["San Francisco", 8], ["New York", 3], ["Madison", 2], ["Washington", 3], ["Tulsa", 3]]
array2 = ["Durham", "Rochester", "New York", "Tulsa", "Kenner", "Washington", "Linton", "Kansas City", "San Francisco", "Madison"]
mapping = Hash[array1]
mapping.default = 0
array2.each do |city|
puts "#{city} (#{mapping[city]})"
end
I'd suggest looking at array uniq, and intersection (&).

Resources