I have an app in backbone where I want to find inside a Json some records and print out.
my JSON is like this:
[
{
"id" : "r1",
"hotel_id" : "1",
"name" : "Single",
"level" : "1"
},
{
"id" : "r1_1",
"hotel_id" : "1",
"name" : "Double",
"level" : "2"
},
{
"id" : "r1_3",
"hotel_id" : "1",
"name" : "Double for single",
"level" : "1"
},
{
"id" : "r1_4",
"hotel_id" : "1",
"name" : "Triple",
"level" : "3"
},
{
"id" : "r2",
"hotel_id" : "2",
"name" : "Single",
"level" : "1"
},
{
"id" : "r2_1",
"hotel_id" : "2",
"name" : "Triple",
"level" : "1"
}
]
I want to combine each room for each hotel for level. Each hotel can have more rooms combination but unique level.
My goal is to print something like this for hotel where id = 1 (same for the other with different combination):
First combination for hotel with id 1:
Room "Single", "level" : "1" , "hotel_id" : "1"
Room "Double", "level" : "2" , , "hotel_id" : "1"
Room "Triple", "level" : "3" , , "hotel_id" : "1"
Second combination for hotel with id 1:
Room "Double for single", "level" : "1" , "hotel_id" : "1"
Room "Double", "level" : "2" , , "hotel_id" : "1"
Room "Triple", "level" : "3" , , "hotel_id" : "1"
Each hotel can have more rooms of some level, but I want to construct combination with one room foreach hotel.
This is my parsing in backbone but I have only retrieve the JSON inside allRooms.
//each for all my hotel
_.each(this.collection.models, function(hotel) {
var rooms = new Array();
rooms.push(allRooms.where({hotel_id : hotel.id}));
//this is where I have to construct my combination
//this is the array for each combination
hotel.get('rooms').push(rooms);
});
How to construct this combination?
Based on @Bergi's answer, I came up with this. It should solve your problem.
Here is a demo: http://plnkr.co/edit/NHE9V5?p=preview
Updated I have modified some things to accomodate your separate JSON files.
Cartesian Product Helper (http://en.wikipedia.org/wiki/Cartesian_product)
function cartesian(arg) {
arg = arg || [];
var r = [],
max = arg.length - 1;
function helper(arr, i) {
for (var j = 0, l = arg[i].length; j < l; j++) {
var a = arr.slice(0); // clone arr
a.push(arg[i][j]);
if (i == max) {
r.push(a);
} else helper(a, i + 1);
}
}
if(arg.length > 0)
helper([], 0);
return r;
}
Nested Collection Solution
HotelModel = Backbone.Model.extend({
initialize: function() {
// because initialize is called after parse
_.defaults(this, {
rooms: new RoomCollection()
});
},
parse: function(response) {
if (_.has(response, "rooms")) {
this.rooms = new RoomCollection(response.rooms, {
parse: true
});
delete response.rooms;
}
return response;
},
toJSON: function() {
var json = _.clone(this.attributes);
json.rooms = this.rooms.toJSON();
return json;
},
addRoom: function(rooms, options) {
return this.rooms.add(rooms, options);
},
removeRoom: function(rooms, options) {
return this.rooms.remove(rooms, options);
},
createRoom: function(attributes, options) {
return this.rooms.create(attributes, options);
},
getCombinations: function() {
return cartesian(_.values(this.rooms.groupBy('level')));
}
});
RoomModel = Backbone.Model.extend({});
HotelCollection = Backbone.Collection.extend({
model: HotelModel,
getAllCombinations: function(){
return this.map(function(hotel){
return _.extend(hotel.toJSON(), {
combinations: hotel.getCombinations()
});
});
}
});
RoomCollection = Backbone.Collection.extend({
model: RoomModel,
getRoomsByHotelId: function(hotelId) {
return this.where({
hotelId: hotelId
});
}
});
Loading of the Separate JSON
var hotels = new HotelCollection([], {
url: 'hotels.json'
});
var rooms = new RoomCollection([], {
url: 'rooms.json'
});
hotels.fetch({
success: function() {
rooms.fetch({
success: function() {
hotels.each(function(hotel) {
hotel.addRoom(rooms.getRoomsByHotelId(hotel.id));
});
// all done here
var combos = hotels.getAllCombinations();
$(function() {
$('body').append('<pre>' + JSON.stringify(combos, null, 2) + '</pre>');
});
}
});
}
});
hotels.json
[{
"id": 1,
"name": "Hotel One"
}, {
"id": 2,
"name": "Hotel Two"
}, {
"id": 3,
"name": "Hotel Three"
}]
rooms.json
[{
"level": 1,
"name": "Room A",
"hotelId": 1
}, {
"level": 1,
"name": "Room B",
"hotelId": 1
}, {
"level": 2,
"name": "Room A",
"hotelId": 1
}, {
"level": 2,
"name": "Room B",
"hotelId": 1
}, {
"level": 1,
"name": "Room A",
"hotelId": 2
}, {
"level": 1,
"name": "Room B",
"hotelId": 2
}, {
"level": 2,
"name": "Room A",
"hotelId": 2
}, {
"level": 2,
"name": "Room B",
"hotelId": 2
}, {
"level": 1,
"name": "Room A",
"hotelId": 3
}, {
"level": 1,
"name": "Room B",
"hotelId": 3
}, {
"level": 1,
"name": "Room C",
"hotelId": 3
}]
First you should split your rooms list by hotels and levels:
var rooms = _(allRooms.groupBy, "hotel_id");
for (var hotelid in rooms)
rooms[hotelid] = _.groupBy(rooms[hotelid], "level");
The "combinations" you're looking for is the cartesian product of the levels (for each hotel). You can use this helper function for example. Use it like this:
_.each(this.collection.models, function(hotel) {
var levels = rooms[hotel.id];
var combinations = cartesian(_.values(levels));
// put them on the hotel
});