Efficient way to randomize data model in Rails - ruby-on-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.

Related

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.

Ranked Model getting started

I'm trying to use the gem 'ranked-model' in Rails 3.2.
This is the model:
class Costproject < ActiveRecord::Base
include RankedModel
scope :active, where("coststatus_id IN (?)", [2, 3, 4, 5, 6] ).where("project_year = ?", Time.now.year + 1)
ranks :row_order,
:scope => :active
I ran this in the Rails console:
Costproject.rank(:row_order).all
After, the column row_order is blank in all the records.
What am I doing wrong?
Thanks for the help!
UPDATE1
I believe now that Costproject.rank(:row_order).all is used to list the projects. I thought it was a command to seed the numbers in the list. Maybe I don't have to do that?
Per our conversation via comments. The problem looks like you need to seed your database before you can use the rank command.
So you can seed your dev database a few ways, via rails console, using your app or modifying your seeds.rb file.
I'm going to suggest you use your seeds.rb file because it's idempotent ( http://en.wikipedia.org/wiki/Idempotence )
So open db/seeds.rb or create one if you don't have one.
Create a few records:
Costproject.create(rank_order: 3, coststatus_id: 2, project_year: 2016)
Costproject.create(rank_order: 2, coststatus_id: 2, project_year: 2016)
Costproject.create(rank_order: 1, coststatus_id: 2, project_year: 2016)
This will insert 3 records into costprojects table. You MUST have the coststatus_id and project_year set to those values per your scope. These won't work:
Costproject.create(rank_order: 10)
Costproject.create(rank_order: 20, coststatus_id: 10)
Costproject.create(rank_order: 30, coststatus_id: 2, project_year: 2013)
then via terminal you can run:
bundle exec rake db:seed
This rake task will run your seed file as if you were manually typing it in your rails console.
Now you should be able to do Costproject.rank(:row_order).all. The first model should have an id of 3, assuming you ran your seed file on an empty database.
For more info:
What is the best way to seed a database in Rails?
http://railscasts.com/episodes/179-seed-data
http://www.jasonwieringa.com/rake_rails_and_databases/

Prepend multiple data to beginning of Core Data (Swift)

Could someone give some guidance on how to prepend newly ingested data to Core Data in the correct index order?
I have Core Data with an attribute, contentID. The objects are listed as: 5, 4, 3, 2, 1, 0
I query a web database that pushes updates since the highest contentID, 5, and it returns an array with updates: 7, 6
If you simply append it to Core Data, the order will go: 5, 4, 3, 2, 1, 0, 7, 6
How do you prepend it to the front correctly?
And also how do you prepend it in the correct order so that when you prepend the first new object, 7, and then prepend the second item, 6, it doesn't go: 6, 7, 5, 4, 3, 2, 1, 0
Hope this makes sense... Thanks!
In general, you should not concern yourself with the order in which objects are written to or stored in the database. Instead, specify the sort order in which you wish to receive the data when you fetch it from the database.
The sort order for a fetch is determined by the sortDescriptors property of the NSFetchRequest. Each NSSortDescriptor specifies the key to determine the order, and whether the results should be in ascending order (v. descending).
So when you perform a fetch, you should in your case specify:
let sort = NSSortDescriptor(key:"contentID", ascending:false)
fetchRequest.sortDescriptors = [sort]
let fetchedResults = managedObjectContent.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject]

How to count records and group by date including days with 0 records?

In ActiveRecord, how would one query an Event model to get a number of records created every day, including days where no records were created?
For example, if I created events on four out of seven days this week, I would want it to return [2, 4, 0, 1, 0, 7, 3].
So far my query looks like this:
Event.where(name: name).order('date(created_at)').group('date(created_at)').count.values, but it only returns [2, 4, 1, 7, 3] which will not work when creating a graph of events created over the last X days.
Thanks!
Don't try to roll this on your own, just use this library. It handles all the edgecases for you, including differening weekly start days, ranges that begin with no records, etc.
The answer to your specific question would be:
# Unscope is optional, but probably what you want if there is a default_scope.
Event
.unscoped
.group_by_day(:created_at)
.where(name: name)
.count
but if you wanted it for a time range make sure not to put it in the where clause because that will miss the empty values at the beginning and end of the range.
range = 10.days.ago..2.days.ago
Event
.unscoped
.group_by_day(:created_at, range: range)
.where(name: name)
.count
And it will include values across the entire range.
Try this,
Once you have got the group counts,
event_counts_hash = Event.where(name: name).order('date(created_at)').group('date(created_at)').count
record_count_array = [0] * 7
event_counts_hash.each { |k, v| count_array[k.cwday] = v }
You should get your weekday counts in your count_array.
I recently open sourced chart_data.js (WIP), a client-side micro library which handles those cases in which you need to represent time series data but the series are incomplete.
Example:
var from = moment('20140101', 'YYYYMMDD');
var to = moment('20140101', 'YYYYMMDD');
var sampleData = [['2014', '01', '01', '100'], ['2014', '03', '01', '300'], ['2014', '04', '01', '100']];
var chartData = new ChartData(sampleData, from, to, 'months');
var data = chartData.data;
// [1388534400000, 100], [1391212800000, 0], [1393632000000, 300], [1396310400000, 400]
Have a look at it, I hope you find it useful and will appreciate your feedback.

Resources