I want to display value of collection by passing their respective attribute name.
#mandates is the result of an active-record query.
#tabattributes contains array of attribute names previously selected by users.
The code below show field attributes but I want the value of these field instead.
I've tried several syntaxes but errors occurs each time.
How can I modify my code to do that?
#mandates.map do |f|
#tabattributes.each { |att| " #{att} "}
end
If #mandates is a result set that contains models with attributes a, b, and c and #tabattributes is the array %w{a b} (i.e. you want to extract a and b from each element of #mandates) then:
a = #mandates.map { |m| m.attributes.slice(*#tabattributes) }
will give you an array of hashes with keys 'a' and 'b'. For example:
#tabattributes = %w{id created_at}
slices = #mandates.map { |m| m.attributes.slice(*#tabattributes) }
# slices is now like [ { 'id' => ..., 'created_at' => ... }, ... ]
If you only want the values and don't care about the keys then perhaps this will work for you:
#mandates.map { |m| m.attributes.slice(*#tabattributes).values }
That would give you an array-of-arrays. The first array-of-hashes would probably be easier to work with though.
If you can get at #mandates before accessing the database then you could slice out just the columns you're interested inside the database with something like this:
#mandates = Mandate.select(#tabattributes)
slices = #mandates.map(&:attributes)
If I understand you right, you have an array of elements, and you want to have an array containing the name of each element, is that it ? If yes, then array.map {|elem| elem.name} should do it. There is a shorter form (array.map(&:name)) which does the same, if you're interested in how this is working, I can detail.
Related
I'm trying to set up a table_for in my active admin project which displays information based on methods I pass to it. I'm setting it up so that in the model, there is an array of arrays. The arrays within the array contain first the label, then the method meant to be run in the column. I'm trying to set it up this way:
panel "Acquired Shares" do
table_for shareholder.acquired_shares_transactions do
shareholder.acquired_shares_info.each do |section|
column (section[0]) { |transaction| section[1] }
end
end
end
Here is the code of the method which returns the array of arrays:
def acquired_shares_info
data = [[:label, transaction.event.to_s], [:amount_of_shares, transaction.amount_of_shares],
[:share_price, transaction.event.share_price],
[:total_price, (transaction.amount_of_shares * transaction.event.share_price)],
[:occurred_on, transaction.event.occurred_on],
[:from_shareholder, transaction.event.from_shareholder],
[:share_transaction_action, transaction.event.share_transaction_action.name],
[:share_transaction_type, transaction.event.share_transaction_type.name]]
return data
end
This is all is meant to create a column for each label and method I specify in the array. However, I am stuck on how to pass the labels and methods from the array into the column for the table. The way that I try here keeps throwing the error "No block given" on the array. Anyone have ideas on how to set this up?
Thank you!
Defer evaluation of the method by wrapping it in a proc. Pass the proc as the last parameter instead of specifying a block, Ruby will substitute it for you.
data = [[:label, proc { |transaction| transaction.event.to_s }], ...
...
column section[0], section[1]
...
I have a table on Db with two fields: group and text
I want to retrieve all records and convert into an hash grouped by the group field, and containing an array of all texts, something like this:
{
'group_1': ['text1','text2',...]
'group_2': ['text1','text2',...]
'group_3': ['text1','text2',...]
}
I acomplish it partially with this
MyModel.all.group_by(&:group)
but it returns an array with all the full AR object, i just want an array of all the text strings
I was trying with map but I can figure out how to do this without using each
Any idea?
Try the following:
MyModel.all.select([:text, :group]).group_by(&:group)
If you wanted to use an iterator (ex: each/map), here is how you would do it:
grouped_models = MyModel.all.group_by(&:group)
grouped_models.map do |group, models|
grouped_models[group] = models.map(&:text)
end
grouped_models
# => { 'group_1' => ['text1', 'text2'], 'group_2' => ['text'] }
I've an array of hash entries, and want to filter based on a paramater passed into the function.
If there are three values in the hash, A, B, and C, I want to do something similar to:
data = [{A:'a1', B:'b1', C:'c1'},
{A:'a1', B:'b2', C:'c1'},
{A:'a1', B:'b2', C:'c2'},
{A:'a2', B:'b1', C:'c1'},
{A:'a2', B:'b2', C:'c1'}]
data.find_all{ |d| d[:A].include?params[:A] }
.find_all{ |d| d[:B].include?params[:B] }
.find_all{ |d| d[:C].include?params[:C] }
find all where A == 'a1' AND B='b2'
so for above I get:
{A:'a1', B:'b2', C:'c1'} and {A:'a1', B:'b2', C:'c2'}
* put if / else statements, like params.has_key? 'B' then do something.
* modify my code each time a new type of value is added to the Hash map (say now I have 'D').
Note: The key of the hash is a symbol, but the value is a string and I want to do a "contains" not "equals".
I think of it as SQL Select statement with where zero or more '==' clause
If I understand your question correctly, then I believe that the select method of Array class may be helpful to you.
select takes a block of code which is intended to be a test condition on each element in your array. Any elements within your array which satisfy that test condition will be selected, and the result is an array of those selected elements.
For example:
arr = [ 4, 8, 15, 16, 23, 42 ]
result = arr.select { |element| element > 20 }
puts result # prints out [23, 42]
In your example, you have an array of hashes, which makes it only slightly more complicated than my example with a simple array of numbers. In your example, we have:
data = [{A:'a1', B:'b1', C:'c1'},
{A:'a1', B:'b2', C:'c1'},
{A:'a1', B:'b2', C:'c2'},
{A:'a2', B:'b1', C:'c1'},
{A:'a2', B:'b2', C:'c1'}]
I believe what you want your code to do is something like: Select from my data array all of the hashes where, within the hash, :A equals some value AND :B equals some other value.
Let's say you want to find all of the hashes where :A == 'a1' and :B == 'b2'. You would do that like this:
data.select { |hash_element| hash_element[:A] == 'a1' && hash_element[:B] == 'b2' }
This line returns to you an array with those hashes from your original data array which satisfy the condition we provided in the block - that is, those hashes where :A == 'a1' and :B == 'b2'. Hope that that helps shed some light on your problem!
More information about the select method here:
http://www.ruby-doc.org/core-2.1.0/Array.html#method-i-select
edited - below is an addition to original answer
To follow up on your later question about if/else clauses and the addition of new parameters... the block of code that you pass to select can, of course, be much more complicated than what I've written in the example. You just need to keep this in mind: If the last line of the block of code evaluates to true, then the element will be selected into the result array. Otherwise, it won't be selected.
So, that means you could define a function of your own, and call that function within the condition block passed to select. For example, something like this:
def condition_test(hash_element, key_values)
result = true
key_values.each do |pair|
if hash_element[pair[:key]] != pair[:value]
result = false
end
end
return result
end
# An example of the key-value pairs you might require to satisfy your select condition.
requirements = [ {:key => :A, :value => 'a1'},
{:key => :B, :value => 'b2'} ]
data.select { |hash_element| condition_test(hash_element, requirements) }
This makes your condition block a bit more dynamic, rather than hard-wired for :A == 'a1' and :B == 'b2' like we did in the earlier example. You can tweak requirements on the fly based on the keys and values that you need to look for. You could also modify condition_test to do more than just check to see if the hash value at some key equals some value. You can add in your if/else clauses in condition_test to test for things like the presence of some key, etc.
I think you want to use .values_at(key)
http://www.ruby-doc.org/core-2.1.0/Hash.html#method-i-values_atvalues_at method
Good day, I was wondering, is it possible if i make a selection with group_by like so
#p = Performance.includes(place: [:category]).
order("places.title ASC").
group_by{|p| p.place.category}
so if i want a specific category to be the first, what do i do?
EDIT 1
in view a parse through the results by #p.each do |p|
The return value of group_by is just a normal hash, so you can apply sort_by on it to place your desired category first:
group_by { |p| p.place.category }.sort_by { |k,v| (k=="category name") ? "" : k }
where category name is the name of the category you want to prioritize (the empty string make it come first in the sort results, everything else will just be sorted alphabetically).
This will transform the hash into an array. If you want to keep the data in hash form, wrap the result in Hash[...]:
Hash[group_by { |p| p.place.category }.sort_by { |k,v| (k=="category name") ? "" : k }]
See also this article on sorting hashes: http://www.rubyinside.com/how-to/ruby-sort-hash
UPDATE:
A slightly less processor-intensive alternative to sorting:
grouped = group_by { |p| p.place.category }
Hash[*grouped.assoc("category name")].merge(grouped.except("category name"))
There might be a simpler way to do this, but basically this prepends the key and value for "category name" to the head of the hash.
Although I think shioyama's answer might help you, I doubt you really need the sort process. As shio correctly states, the return value of your sort_by is a hash. So why dont you just access the value, which you want as the first value, simply by using it as hash-key?
[[{"Postponed"=>10}], [{"Low"=>3}], [{"Medium"=>4}], [{"High"=>5}]]
is the array
how can I get the value corresponding to particular value.
say High returns 5 in this.
or how to convert this array of hashes to an array so that searching becomes easy.
I tried:
find_all { |v| v['name'] == "Low" }
but it says:
cant convert String to Integer
please provide some guidance
How about making a single hash out of it for efficient querying?
arr.flatten.reduce(:merge)
#=> {"Postponed"=>10, "Low"=>3, "Medium"=>4, "High"=>5}
If you have some code like:
array = [[{"Postponed"=>10}], [{"Low"=>3}], [{"Medium"=>4}], [{"High"=>5}]]
Then turn it into an ruby hash:
hash = array.inject({}) {|h, e| h.merge(e.first) }
# => {"Postponed"=>10, "Low"=>3, "Medium"=>4, "High"=>5}
So you can find 'Low' value easily :
hash['Low']
# => 3
EDIT: The answer of Mark Thomas is pretty great, and shorter than the inject since it does the same thing. He wrote it before I answered. Nice ;)
In the general case, the hashes won't be unique, so you need to filter rather than pick one via indexing. For example, let's say you have this:
arr = [[{:apple => 'abc'}], [{:banana => 'def'}], [{:coconut => 'ghi'}]]
# => [[{:apple=>"abc"}], [{:banana=>"def"}], [{:coconut=>"ghi"}]]
Now let's suppose you want to get the value corresponding to any hash with a :coconut key. Then just use:
arr.flatten.map { |h| h[:coconut] }.compact
# => ["ghi"]
That gives you the list of answers. In this case there's only one matching key, so there's only one entry in the array. If there were other hashes that had a :coconut key in there, then you'd have something like:
# => ["ghi", "jkl", "mno"]
On the whole, though, that's a very unusual data structure to have. If you control the structure, then you should consider using objects that can return you sensible answers in the manner that you'd like, not hashes.
You could simply call #flatten on the original array. That would give you an array of hashes. What I think you would really want is just one hash.
1.8.7 :006 > [[{"Postponed"=>10}], [{"Low"=>3}], [{"Medium"=>4}], [{"High"=>5}]].flatten
=> [{"Postponed"=>10}, {"Low"=>3}, {"Medium"=>4}, {"High"=>5}]
I would ask, what are you doing to get that original structure? Can that be changed?
How about this?
arr = [
[{"Postponed"=>10}],
[{"Low"=>3}],
[{"Medium"=>4}],
[{"High"=>5}]
]
arr1 = []
arr.each{|a|
arr1.push(a[0])
}
Although I wonder if you really just want to get one hash, which you'd do like so:
myHash = {}
arr.each{|a|
a[0].each{|b, c|
myHash[b] = c
}
}
You would then access it like myHash["Postponed"]