Is it possible to do conditional sort in hash? - ruby-on-rails

Is it possible to do conditional sort in hash?
My hash be like:
{1=>"10",2=>"20",3=>"30",4=>"40",5=>"50",6=>"60",7=>"70",8=>"80",9=>"90"}
Desired result is:
{7=>"70",8=>"80",9=>"90",1=>"10",2=>"20",3=>"30",4=>"40",5=>"50",6=>"60"}
add condition in this code
operation_hour_hash.sort{|key,value| key[1]<=>value[1]}

Sure, you could do something like:
>> {1=>"10",2=>"20",3=>"30",4=>"40",5=>"50",6=>"60",7=>"70",8=>"80",9=>"90"}.sort{|p,n| (p[1].to_i>=70 && n[1].to_i<70) ? -1 : (p[1].to_i<70 && n[1].to_i>=70) ? 1 : p[1].to_i <=> n[1].to_i}
=> [[7, "70"], [8, "80"], [9, "90"], [1, "10"], [2, "20"], [3, "30"], [4, "40"], [5, "50"], [6, "60"]]
But, sorting a hash doesn't really make much sense. Before the sort actually takes place it's converted to an array of [key,value] pairs then sorted by -1,0,1 returned from <=>.
If you need sorted hashes you'll need to use something like RubyFacets Dictionary class.

Your sort call is misleading. You are not comparing key and value, you are comparing two different elements of an array. So it should be:
operation_hour_hash.sort{|a,b| a[1]<=>b[1]}
You can implement whatever logic you want in a sort block, as long as it adheres to following:
The block implements a comparison
between a and b, returning -1, 0, or
+1
(taken from: http://ruby-doc.org/core/classes/Array.html#M002185)

You can try something like this to produce a sorted array
operation_hour_hash.sort {|x,y| (x[0]>6?x[0]-6:x[0]+24)<=>(y[0]>6?y[0]-6:y[0]+24)}
>> [[7, "70"], [8, "80"], [9, "90"], [1, "10"], [2, "20"], [3, "30"], [4, "40"], [5, "50"], [6, "60"]]
You can change the conditions to suit your needs.
However be careful that in ruby 1.8, a hash is not ordered by the key. If you convert back to hash, the order is not guaranteed.

Related

2 arrays insert via (column) double dimension array ruby (short method) [duplicate]

I am browsing through the ruby Array iterators. And I can't find what I am looking for and I think it already exists:
I have two arrays:
["a", "b", "c"]
[0,1,2]
And I want to merge as so:
[ [0, "a"], [1, "b"], [2, "c"] ]
I think the iterator exists in the standard library (I used it before) but I am having trouble finding its name.
This should work:
[0,1,2].zip(["a", "b", "c"]) # => [[0, "a"], [1, "b"], [2, "c"]]
From the official documentation of the Array#zip function:
Converts any arguments to arrays, then merges elements of self with corresponding elements from each argument.
This generates a sequence of ary.size n-element arrays, where n is one more than the count of arguments.
For more info and some other examples, refer to:
https://ruby-doc.org/core-2.4.2/Array.html#method-i-zip
You are looking for the zip function
https://apidock.com/ruby/Array/zip
I think you could use https://apidock.com/ruby/Enumerator/each_with_index
See this post difference between each.with_index and each_with_index in Ruby?
or if you have specific values and you want to map them you would use map or zip. It explains it well in this post
Combine two Arrays into Hash

Sending array of nulls in json request - Rails 5

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.

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

Efficient way to randomize data model in Rails

Creating a programming schedule based on videos in object model.
I want to run a task every day to shuffle this model so the programming each day would be different.
I am aware of
product.shuffle.all for ex. but I want the order to be saved one time each day to do so vs on each server call.
I am thinking to add an attribute to each product, named order which would be an integer to order by. How would I shuffle just product.order for all products in this case?
Would this be the most efficient way? Thanks for the help!
You could use the random parameter of shuffle. It allows for stable randomization:
# When the Random object's seed is different, multiple runs result in different outcome:
pry(main)> [1,2,3,4,5,6,7].shuffle(random: Random.new)
=> [5, 6, 3, 4, 1, 7, 2]
pry(main)> [1,2,3,4,5,6,7].shuffle(random: Random.new)
=> [1, 7, 6, 4, 5, 3, 2]
# When the Random object is created with the same seed, multiple runs return the same result:
pry(main)> [1,2,3,4,5,6,7].shuffle(random: Random.new(1))
=> [7, 3, 2, 1, 5, 4, 6]
pry(main)> [1,2,3,4,5,6,7].shuffle(random: Random.new(1))
=> [7, 3, 2, 1, 5, 4, 6]
By basing the seed e.g. on the number of day in the year you can determine when the results randomization changes. You can (obviously) restore the randomization for any given day if you need to do so.
What I think you want to do would be best solved with a combination of the gem paper_trail along with your product.shuffle.all and an update_attributes call to the DB. That way you can view past versions as they are updated in your DB.

Tag "metadata" onto an array in ruby

I have some code which assembles two-dimensional arrays to be sent to the Spreadsheet gem's excel-building methods. I'd like to "tag" some subarrays (corresponding to rows) with formatting codes like {:color => "red"}.
Can anyone see a way of doing this? I could achieve a similar result by storing a seperate object which has the formatting option and (for example) the indexes to all rows i want to apply that format to. But it would be nicer if I could stick it straight onto the row itself as i build my data.
One thing that occurred to me is to use some kind of namespaced hash as the last entry in an array, if i want to format it, and then strip that out again in the spreadsheet builder. But, this seems risky as it's then in the array's actual data. Is there any kind of instance variable or something i can tap into with an array to "shove" my metadata in there?
I'm using Rails 2.2 for this app, in case that's relevant.
Since each row contains data and possibly some metadata, it seems natural to represent all rows as an Array of Hashes, where each Hash contains a :data key and a :metadata key. The latter can be omitted or can just point to an empty Hash if you do not have any metadata for the row, whichever you prefer.
I have no experience with the Spreadsheet gem, but I assume it requires an Array of Arrays as input, which you can create in a straightforward manner from the Array of Hashes as shown in the code below.
rows = [
{
data: [2, 3, 5],
metadata: { color: 'red' }
},
{
data: [7, 11, 13],
metadata: {}
}
]
# Transform into Array of Arrays, removing all metadata
rows.map { |row| row[:data] }
# => [[2, 3, 5], [7, 11, 13]]

Resources