Ruby on Rails get key by value from two dimensional array - ruby-on-rails

I have a two dimensional array that looks like this:
TITLETYPE = [['Prof.', '4'],
['Dr.', '3'],
['Mrs.', '2'],
['Ms.', '1'],
['Mr.', '0']]
I need to get the key for value 1 for example (which should be 'Ms.')
How should I go about doing that?

TITLETYPE.select{ |x| x[1] == '1' }.first.first
How this works
You can use Array's select method to find the row you're looking for. Your rows ar arrays with two elements each (element 0 and element 1), so you need to look for the row in which the second element (element 1) is equal to the value you're looking for (which is the string "1"):
TITLETYPE.select{ |x| x[1] == "1" }
This will return an array with only one row:
[["Ms.", "1"]]
To get the first and only value from that array, use Array's first method, which will return:
["Ms.", "1"]
Then, from that, obtain the first value from the two values with first again:
"Ms."

Actually, sounds like Array#rassoc is perfect for you.
TITLETYPE.rassoc('1')[0] # => 'Ms.'
See the documentation at Ruby-doc.

More naturally, you should keep such information as a hash. If you often want key-to value, and key-to value is unique, then create a hash:
TYTLETYPEHASH = Hash[TYTLETYPE.map(&:reverse)]
and access it:
TYTLETYPEHASH['1'] # => 'Ms.'
or create a hash like:
TYTLETYPEHASH = Hash[TYTLETYPE]
and access it:
TYTLEHASH.key('1') # => 'Ms.'

I had a similar issue, and resolved it by simply using something like this:
<% Array.each do |value| %>
and accessing each element using <%= value[:keyname] %>
i.e.
An array which looks like this (using .inspect)
[{:id=>1, :title=>"ABC"}, {:id=>2, :title=>"XYZ"}]
can become a HTML selection/dropdown box with:
<select name="ClassName[TargetParameter]">
<% Array.each do |value| %>
<option value="<%= value[:id] %>"><%= value[:title] %></option>
<% end %>
</select>

Related

How to populate a select_tag with an array of hashes?

In a Rails 3.2 app I'm trying to add a select field that takes its data from an external API call. This data is returned as an array of hashes:
[{"name"=>"NameA", "id"=>"001"}, {"name"=>"NameB", "id"=>"002"}]
How can I use this data to construct a select field that looks something like:
<select>
<option value="001"> NameA </option>
<option value="002"> NameB </option>
</select>
EDIT:
Thanks to the suggestions below I've tried the following:
A:
<%= select_tag 'model[field]', options_from_collection_for_select(#hash, :id, :name) %>
Gives an error:
undefined method `name' for {"name"=>"NameA", "id"=>"001"}:Hash
B:
<%= select_tag 'model[field]', options_from_collection_for_select(#hash) %>
Fixes the error but generates the wrong markup
<option value="{"name"=>"NameA", "id"=>"001"}"> {"name"=>"NameA", "id"=>"001"}</option>
So I think my problem is formatting the array of hashes correctly, and I don't know enough about manipulating arrays of hashes to work out how to do this.
Unless I'm looking in completly the worng direction, I think the key to this problem is to reformat the array at the top of this question to give:
{"NameA" =>"001", "NameB" =>"002"}
Is this even possible? And if so, how?
If you have array of hashes like this:
#people = [{"name"=>"NameA", "id"=>"001"}, {"name"=>"NameB", "id"=>"002"}]
You can use options_for_select helper with collect method like this:
= select_tag "people", options_for_select(#people.collect {|p| [ p['name'], p['id'] ] })
And its done :-).
A better way to do it in only one command:
<%= select_tag "model[field]", options_for_select(#array_of_hashes.map { |obj| [obj['name'], obj['id']] }) %>
With your example hash:
irb> #array_of_hashes = [{"name"=>"NameA", "id"=>"001"}, {"name"=>"NameB", "id"=>"002"}]
=> [{"name"=>"NameA", "id"=>"001"}, {"name"=>"NameB", "id"=>"002"}]
irb> #array_of_hashes.map { |obj| [obj['name'], obj['id']] }
=> [["NameA", "001"], ["NameB", "002"]]
The easiest way to use Hashes in selects, for me is:
The hash:
REVISION_TYPES={"S"=>"Stock", "T"=>"Traducción"}
In the form:
select_tag(:revision_type,options_for_select(REVISION_TYPES.invert.to_a))
You can use options_for_select for this purpose. It takes a two dimensional Array. You can convert your hash like so:
data = [{"name"=>"NameA", "id"=>"001"}, {"name"=>"NameB", "id"=>"002"}]
data_for_select = data.each { |hash| [hash['name'], hash['id']] }
options_for_select(data_for_select)
As a side note to options_from_collection_for_select, it is used in combination with objects. It iterates through the objects and sends a message for the label and one for the id.
Ok, I eventually figured out a solution that works, though it may not be the best.
$ #array_of_hashes = [{"name"=>"NameA", "id"=>"001"}, {"name"=>"NameB", "id"=>"002"}]
=> [{"name"=>"NameA", "id"=>"001"}, {"name"=>"NameB", "id"=>"002"}]
$ #formatted_array_of_hashes = #array_of_hashes.each.map{ |h| { h["name"] => h["id"] }}
=> [{"NameA" => "001"}, {"NameB" => "002"}]
$ #merged_hash = Hash[*#formatted_array_of_hashes.map(&:to_a).flatten]
=> {"NameA" => "001", "NameB" => "002"}
I was then able to create a select field
<%= select_tag 'model[field]', options_for_select(#merged_hash) %>
that generates the correct markup
<select>
<option value="001">NameA</option>
<option value="002">NameB</option>
</select>
A little convoluted, but its working. I welcome any improvements.
I'm not really sure why, but none of the answers worked for me. maybe because of Rails 5, so I figured it out by myself and I'm happy to share.
It's now pretty but works well.
It's an Array of Hashes from an external API (Pipedrive) that I want to associate with the user's table:
This is in my form:
<%= form.collection_select(:id_user_pipedrive, Owner.all_id_name, :id, :name, :include_blank => '') %>
And this is in my fake model:
UserPipedrive = Struct.new(:id, :name)
def self.all_id_name
all["data"].map { |d| UserPipedrive.new(d["id"],d["name"]) }
end
Hope it helps someone.

Ruby - bad output from arrays

I have a form with a lots of these text inputs:
<%= text_field_tag 'name[seq]['+dat.id.to_s+']', dat.seq%>
After send this form I want to save them to database, I try to get the values from inputs in each loop:
unless params[:name].nil?
params[:name][:seq].each_with_index do |sq, i|
puts sq
end
end
But the output in terminal is wrong, for example if I have an input with the values
<%= text_field_tag 'name[seq][25]', 3%>
So I am going to expect the output is 3, but I will get to terminal this:
25
3
Is here something important, what I don't see?
Yes, you are missing something. Within your each_with_index block, sq will be an array and that's why you get that output.
So, what's going on here? Well, your params will contain this:
"name" => { "seq" => { "25" => "3" } }
And that means that params[:name][:seq] is this:
{ "25" => "3" }
Then you apply each_with_index to that to iterate through the Hash. If you do it like this:
params[:name][:seq].each_with_index do |(k,v), i|
puts "-#{k}-#{v}-"
end
you'll see what's going on.
If you just want the 3 then you can iterate over params[:name][:seq] as above and just look at v inside the block or, if you know what the '25' is some other way, you could just go straight there:
three = params[:name][:seq]['25']

Ruby on Rails: Interpreting a form input as an integer

I've got a form that allows the user to put together a hash.
The hashes desired end format would be something like this:
{1 => "a", 2 => "x", 3 => "m"}
I can build up something similar by having lots of inputs that have internal brackets in their names:
<%= hidden_field_tag "article[1]", :value => a %>
However, the end result is that builds a hash where all the keys are strings and not integers:
{"1" => "a", "2" => "x", "3" => "m"}
I'm currently fixing this by generating a new hash in the controller by looping over the input hash, and then assigning that to params. Is there a cleaner, DRYer way to do this?
Your params will always come in with string keys and values. The easiest way to fix this is to either write your own extension to Hash or simply inject as required:
numeric_keys = params['article'].inject({ }) do |h, (k, v)|
h[k.to_i] = v
h
end
Then you have a hash with the keys converted to integer values, as you like.
A simple extension might be:
class Hash
def remap_keys
inject({ }) do |h, (k, v)|
h[yield(k)] = v
h
end
end
end
This is much more generic and can be used along the lines of:
params['article'].remap_keys(&:to_i)
That depends a bit what you want to use it for. Maybe it is easier to just use strings as keys, and do the "conversion" when accessing the array (or not at all)?
It is also possible to build an array using something like
<%= hidden_field_tag "article[]", :value => "x" %>
this will return "article" as an array, and you can access it directly by index. However, there is no way to influence the position - the array will contain all values in order of appearance.
Lastly, you can make your own version of Hash or just modify the keys, as has been explained.

Passing hash as values in hidden_field_tag

I am trying to pass some filters in my params through a form like so:
hidden_field_tag "filters", params[:filters]
For some reason the params get changed in the next page. For example, if params[:filters] used to be...
"filters"=>{"name_like_any"=>["apple"]} [1]
...it gets changed to...
"filters"=>"{\"name_like_any\"=>[\"apple\"]}" [2]
note the extra quotations and backslashes in [2] when compared to [1].
Any ideas? I'm attempting to use this with searchlogic for some filtering, but I need it to persist when I change change objects in forms. I would prefer not to have to store it in session.
My solution was just to re-create each of param with key-value pair:
<% params[:filters].each do |key,value| %>
<%= hidden_field_tag "filters[#{key}]",value %>
<% end %>
You actually want/need to 'serialize' a hash using hidden fields.
Add this to your ApplicationHelper :
def flatten_hash(hash = params, ancestor_names = [])
flat_hash = {}
hash.each do |k, v|
names = Array.new(ancestor_names)
names << k
if v.is_a?(Hash)
flat_hash.merge!(flatten_hash(v, names))
else
key = flat_hash_key(names)
key += "[]" if v.is_a?(Array)
flat_hash[key] = v
end
end
flat_hash
end
def flat_hash_key(names)
names = Array.new(names)
name = names.shift.to_s.dup
names.each do |n|
name << "[#{n}]"
end
name
end
def hash_as_hidden_fields(hash = params)
hidden_fields = []
flatten_hash(hash).each do |name, value|
value = [value] if !value.is_a?(Array)
value.each do |v|
hidden_fields << hidden_field_tag(name, v.to_s, :id => nil)
end
end
hidden_fields.join("\n")
end
Then, in view:
<%= hash_as_hidden_fields(:filter => params[:filter]) %>
This should do the trick, even if you have a multilevel hash/array in your filters.
Solution taken http://marklunds.com/articles/one/314
I just wrote a gem to do this called HashToHiddenFields.
The core of the gem is this code:
def hash_to_hidden_fields(hash)
query_string = Rack::Utils.build_nested_query(hash)
pairs = query_string.split(Rack::Utils::DEFAULT_SEP)
tags = pairs.map do |pair|
key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
hidden_field_tag(key, value)
end
tags.join("\n").html_safe
end
Here's how I managed to pass a parameter value through my view - that is, from View A through View B and on to the controller:
In View A (index):
<%= link_to 'LinkName', {:action => "run_script", :id => object.id} %>
In View B (run_script):
<%= form_tag :action => 'index', :id => #object %>
<%= hidden_field_tag(:param_name, params[:id]) %>
In the controller:
Just reference params[:param_name] to make use of the value.
The key transition that wasn't documented anywhere I could find is where {... :id => object.id} from View A is passed on to View B as <%... :id => #object %>, which View B then passes on to the controller as (:param_name, params[:id]) through the hidden_field_tag construct.
I didn't see this documented anywhere but after perusing several posts across several sites including this post (whose syntax provided the key inspiration), the solution finally gelled. I've seen the caveats on hidden fields pertaining to security but have found no other way to do this given my current design, such as it is.
it's because when you convert in HTML with your hidden_field_tag, the backquote is add. After when you received it like a string not a Hash.
The Hash type can't exist in HTML. You have only string. So if you want pass your hash (not recommend by me), you need eval it when you received it. But can be a big security issue on your application.
As a caveat to Vlad's answer, I had to use raw:
<%= raw hash_as_hidden_fields(:filter => params[:filter]) %>
to get it to work in Rails 3.1.1. Essentially, the text being output was being escaped, eg., "<" becoming "&lt".
Assuming the hash is strings, symbols, numbers, and arrays, you can call eval to convert the params string of the hash from the hidden_fields form back into a hash in the controller. Then the backslash escape characters for the quotes added are no longer an issue:
hash = eval(params["hash_string"].to_s)
Credit to the following article for helping identify this simple solution for my case:
How do I convert a String object into a Hash object?
Keep in mind the contents of the params should be cleaned with .require and .permit.

select_tag is sorting (strangely) [Rails]

I have a select box that looks like this (within a form_for)
<%=f.select(:whatever_id, {"blah"=>0, "blah2"=>1, "blah3"=>2, "blah4"=>3}, {:include_blank => true}) %>
and the output is good, but weird... like this:
<select id="personal_information_whatever_id" name="personal_information[whatever_id]"><option value=""></option>
<option value="1">blah2</option>
<option value="2">blah3</option>
<option value="0">blah</option>
<option value="3">blah4</option></select>
But I want it to go in order... what is happening, and how can I correct it?
Edit: I feel like the answer has to do with this
You can never be guaranteed of any
order with hashes. You could try
.sort() to sort the values in
alphabetical order.
is there anything I can use aside from a hash?
Yes, you should use an array of arrays. The easiest way with your example would be something like this:
<%=f.select(:whatever_id, [["blah", 0], ["blah2", 1], ["blah3", 2], ["blah4", 3]], {:include_blank => true}) %>
This should suffice. Take a look at the docs at api.rubyonrails.com too.
In your model, define your hash (in your case your model is "whatever" and your hash "blahs"):
BLAHS = { "blah"=>0, "blah2"=>1, "blah3"=>2 }
In the select tag where you put your hash, type
Whatever::BLAHS.sort {|a,b| a[1] <=> b[1]}
This creates an array as described in other answers, ordered by the second item (the id/numbers).
After saving, when you pull a Whatever back out of the database and want to show the field blah, do this
Whatever::BLAHS.index whatever.blah
The array of arrays mentioned by others works, but when you want to show a Whatever, how do you show the value of blah in a nice way? I recommend sticking with the hash as it solves this problem.
The problem is that the options parameter is a hash, and hashes have no guaranteed order.
This should work for you
<%= f.select(:whatever_id, ([["blah",0],["blah2",1],["blah3",2]]), {:include_blank => true}) %>
Edit in response to your comment: For collections see collection_select

Resources