Sending array of nulls in json request - Rails 5 - ruby-on-rails

I need to send request to my Rails API with key like: ids: [null, 1, 2, null, 3]. Unfortunately Rails cuts all the nulls from this array so the params[:ids] returns [1, 2, 3]. I need those nulls in the array.
How can I prevent Rails from removing them? I can send empty string instead of null, but it's not very elegant.

In rails 5, intends to not have the same sql injection vulnerabilities and so have removed the deep_munge method that would change an empty array value to nil but have left in the configuration option which produces behavior best described by looking at the tests.
for more info
https://apidock.com/rails/v3.2.8/ActionDispatch/Request/deep_munge
https://til.hashrocket.com/posts/e1bed09363-deepmunge-i-hardly-knew-ye
In application.rb add below line
config.action_dispatch.perform_deep_munge = false
and restart the application

use json structure instead of array
replace
ids: [null, 1, 2, null, 3]
with
ids: {"0": null, "1": 1, "2": 2, "3": null, "4": 3}
And in controller access it like
params[:ids].values
[nil, 1, 2, nil, 3]

In different environment null value is interpreted differently.
I think that the best practice is to replace these entries according the result you want to achieve:
ids.map! { |id| id == null ? nullValue : id }.flatten!
Where nullValue is what you're expecting to have in the array.

Related

In Ruby on Rails is there a better way than using eval() to accses this jsonb dynamically?

I've got some jsonb in a database and I want to write to an attribute dynamically, I know the path to the attribute but it could be any depth and any attribute name.
I know the path to the attribute, however the only way I've found of writing this dynamiaclly is with eval().
eval("self.some_json_column['an_array'][10]['a_different_array'][5]['color'] = 'blue'")
self.save
So I know the depth and the array index, but it could be any depth or index. I can build the string and pass it into eval()
However I know that eval() is a last case scenario and was wonderign if it's possible to build the path and write (in this case that color json attribute) dynamically without using eval()
Thanks.
You can reduce your way through the Hash that is returned by self.some_json_column
*path, target = ['an_array',10,'a_different_array',5,'color']
node = path.reduce(self.some_json_colum, &:[])
node[target] = 'blue' if node
This has no error handling if any part of the path is incorrect
You could also look into dig depending on ruby version like so
node = self.some_json_column.dig(*path)
node[target] = 'blue' if node
This will return nil if any part of the path does not match
Example:
h = {
'an_array' => [0,1,2,3,4,5,6,7,8,9,
{'a_different_array' => [1,2,3,4,5, {'color' => 'orange'}]}
]
}
*path,target = ['an_array',10,'a_different_array',5,'color']
h.dig(*path)[target] = 'blue'
h
#=> {"an_array"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
# {"a_different_array"=>[0, 1, 2, 3, 4,
# {"color"=>"blue"}
# ]}
# ]}

Rail postgres array contains (<#) dont include nil

I have a Matchups table that includes a field that is equipment_id and stores an array
I am trying to search the Matchups table to get any records that have an array where it contains specific equipment ids. For example:
So I am running:
search_equipment_ids = [1, 2, 3, 4, 5]
Matchup.where("equipment_id <# ARRAY[?]", search_equipment_ids)
The following are a few examples of arrays that are stored in the table in equipment_id and what the above query would result in
[1, 2] = True
[3, 5] = True
[1, 2, 3] = True
[1, 2, nil, 3] = False
How can get the search to ignore that there might be a nil included in the array. I am going to make a change to how the array is stored so from here on out there will be no nil values...but in the mean time I need to take these into account.
I am using: https://www.postgresql.org/docs/9.5/static/functions-array.html as a reference.
I dont want to use the && operator because the Matchups table is huge it will return to many results if just comparing if one equipment id is present. I want to only return if all the equipment_ids are contained (excluding any nils).
Thank you!
How about removing the nil values from the array? That's easy:
Matchup.where(..., equipment_ids.compact)

How can I delete certain indeces defined in an array from an other array in ruby

I have a method that iterates over an array, does a bunch of stuff under certain conditions and depending on these conditions I would ALSO like to delete some of the elements. In order to keep track of the indexes I like to delete I converted the each in to an each_with_index loop and store the index of the elements that I like to delete in an array index_array. How can I delete exactly the items on those indexes in my original array? Looping over the index_array and using delete_at would change the original index. Please see below description of the scenario:
> my_array = [0,1,2,3,4,5,6,7]
> delete_these_indexes = [1,2,5]
the desired result is:
> my_array => [0,3,4,6,7,8]
How about this?
my_array = [0, 1, 2, 3, 4, 5, 6, 7]
delete_these_indices = [1, 2, 5]
delete_these_indices.sort.reverse_each {|i| my_array.delete_at(i) }
p my_array
# => [0, 3, 4, 6, 7, 8]
It's important to delete from the end of the array, since deleting an item will change the indices of all subsequent items, ergo sort.reverse_each. If you know the array is already sorted, you can just do reverse_each.
If you don't care bout modifying the delete_these_indices array, you can be somewhat more terse:
delete_these_indices.sort!
my_array.delete_at(i) while (i = delete_these_indices.pop)
Again, you can skip sort! if you know delete_these_indices is already sorted.
keep_these_indexes = my_array.each_index.to_a - delete_these_indexes
#=> [0, 3, 4, 6, 7]
If you wish to modify my_array (which appears to be the case):
my_array.replace(my_array.values_at(*keep_these_indexes))
#=> [0, 3, 4, 6, 7]
If not:
new_array = my_array.values_at(*keep_these_indexes)
See Array#values_at.
delete_these_indexes.each_with_index do |val, i|
my_array.delete_at(val - i)
end
deletes at the desired index taking into account how many have previously been deleted and adjusting for that
https://repl.it/CeHZ
Probably not the best answer, but you can do this as well:
delete_at_indices.each {|ind| my_array[ind] = nil }
my_array.delete(nil)
Takes a first pass to conceptually invalidate the data at the specified indices, then the call to .delete will blow out any values that match what's passed in.
This solution assumes that you can define a value that isn't valid for your array. Using nil will be problematic if you're treating this as a sparsely populated array (where nil is a valid value)
Technically you're iterating through each array once, but that Gentleman's Agreement on what your deletable value might make some people uncomfortable

Ruby on Rails / Active Record: Custom Sorting Order

I have a query that can be sorted by date or name.
I also have a URL parameter which is the ID of a specific record. I need to move this record to the top of the list, but keep the rest unchanged.
For example:
if param = 2, then return [2, 1, 3, 4, 5, 6, 7, 8]
if param = 5, then return [5, 1, 2, 3, 4, 6, 7, 8]
if param = 7, then return [7, 1, 2, 3, 4, 5, 6, 8]
... etc.
I know how to do it with an array, but can I do it with Active Record too? Because I need to do pagination etc. after that.
Thanks!
Foo.order "id = #{param[:param].to_i} desc", :name
I used the .to_i just as a very simple way to ensure this parameter is clean, make sure you do that however you need to so that you're not leaving yourself open to SQL injection.

How do I find matches in two arrays in IRB?

For some reason I can't figure this out. But basically I want to compare to models and see if they have any matching emails. Here's my attempt, but this doesn't for some reason work at all.
>> CardReferral.all.select{|cf|cf.email == CardSignup.all.collect{|cs|cs.email}}
Where I can somehow return the object..
CardReferral.all.map(&:email) & CardSignup.all.map(&:email)
from the rdoc
array & other_array
Set Intersection—Returns a new array containing elements common to the two arrays, with no duplicates.
[ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]

Resources