Skip to content

Instantly share code, notes, and snippets.

@brittanydionigi
Created March 27, 2014 21:12
Show Gist options
  • Save brittanydionigi/9818971 to your computer and use it in GitHub Desktop.
Save brittanydionigi/9818971 to your computer and use it in GitHub Desktop.
making people do my work for me
/* Some boys are playing a hockey game and they each play a certain number of shifts. */
/* For simplicity, we'll say there are 15 different shifts in a game. */
[
{
"player": "Joe",
"shifts": [
{ "shift_start": 3, "shift_end": 5 }, // Joe started playing at shift #3, and stopped after shift #5
{ "shift_start": 7, "shift_end": 11 }
]
},
{
"player": "Marc",
"shifts": [
{ "shift_start": 1, "shift_end": 4 },
{ "shift_start": 9, "shift_end": 13 }
]
},
{
"player": "John",
"shifts": [
{ "shift_start": 4, "shift_end": 8 },
{ "shift_start": 10, "shift_end": 12 }
]
}
]
/* We want to take all of Joe's shifts, and find out which shifts each player overlapped with him on. ie: */
/* Marc played shifts: 3, 4, 9, 10, 11 with Joe */
/* John played shifts: 4, 5, 7, 8, 10, 11 with Joe */
/* How do we do it? */
/* Data modeling can change slightly if need be. */
@whichsteveyp
Copy link

If the data modeling can change, and I hope I don't I come off snobby, why not just store each shift explicitly:

{
   "shifts" : [3,4,5, 7,8,9,10,11]
}

I'm sure at that point you're a _ function or two away from finding out (something like a union with unique between players or something like that).

Or, is the point to solve it with this structure and using your own tools (school, interview, production limitations on adding external libraries, etc)?

@brittanydionigi
Copy link
Author

@brittanydionigi
Copy link
Author

Stephen - this is possible. The reason it's not set up like that now is because most of the time I really only need the start & end points of a shift. This is one rare exception where I need to get all the in-between-stuff. Also, the scale of this is going to be more like...20-40 shifts per player, about 40 players, and shifts between 0 and at least 3600, as opposed to 1-15.

@alecperkins
Copy link

Because I’m that guy when it comes to CoffeeScript (and Underscore):

player_data = […above list…]
players = {}
_.each player_data, (player) ->
    shift_list = []
    # Create a single Array with all of the shifts enumerated for that player.
    _.each player.shifts, (shift) ->
        shift_list.push([shift.shift_start..shift.shift_end]...)
    players[player.player] = shift_list

console.log _.intersection(players['Joe'], players['Marc'])
console.log _.intersection(players['Joe'], players['Marc'], players['John'])

Edit: since I’m trying to be less obnoxious about CoffeeScript, a JS version (Underscore is pretty great):

player_data = […above list…]
var players = {};
// Map an enumerated list of shifts to each player name (likely will be
// ordered, but that doesn’t matter)
_.each(player_data, function(player){
    var shift_list = _.map(player.shifts, function(shift){
        // Add each shift as a full range.
        return _.range(shift.shift_start, shift.shift_end + 1);
    });
    // The shift_list is a nested Array, so flatten it. As long as the starts
    // and ends don't overlap, there won't be duplication (though doesn't
    // matter for intersection).
    players[player.player] = _.flatten(shift_list);
});

// _.intersection allows for any number of lists to be compared. Yay.
console.log(_.intersection(players['Joe'], players['Marc']));
// [ 3, 4, 9, 10, 11 ]
console.log(_.intersection(players['Joe'], players['Marc'], players['John']));
// [ 4, 10, 11 ]

Edit 2: oops, clarified player_data
Edit 3: comments (I’m stuck in a boring meeting pretending to take notes)

@brittanydionigi
Copy link
Author

Oh gosh I don't know coffeescript. I don't even know how to write my player_data. UNEXPECTED IDENTIFIERS EVERYWHERE.

@whichsteveyp
Copy link

The first link you posted essentially does the same thing I was suggesting - except it creates the new data structure from your data structure. They just stores each range on object.[playerName] and then loops through the other players and compares the _.intersection. The second link might be a lot faster or efficient, although at a glance it's a lot trickier to read.

If your database can store the ranges on each player for you (e.g. storage space less of a concern) then you wouldn't have to perform the range production in your code. Maybe saving some performance there, I'm not a performance guru though so it might be negligible.

Also, if you do end up storing a range on each player but are typically only working with a start and end input, you could change the data to something like:

{
  'player': 'Joe',
  'shifts': [0,1,2],
  'start': 0,
  'end': 2
}

In which case writing updates you'd just need to make sure to replace the shifts with a new range if start and end changed.

edit: TL;DR - if everyone ends up using _.intersection on a range that they calculate, my point is why not just calculate the range once upon create or update, and then save the ops later if you allow lots of intersection calculations in your app

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment