0

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?

Alessandro Minoccheri
  • 35,521
  • 22
  • 122
  • 171

2 Answers2

3

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
}]
Brian Lewis
  • 5,739
  • 1
  • 21
  • 28
  • Yes is better if you could put it in backbone is simple to try it if you can into your browser – Alessandro Minoccheri Jul 03 '13 at 17:43
  • +1 for fixing my code and integrating it into Backbone. I should've looked better at my own `cartesian` function, I had forgotten that it doesn't take arrays (or at least needed to be adapted). – Bergi Jul 03 '13 at 18:20
  • Perfect work... if I take this json from external is the same thing right? Because is data from server and I store it into json file – Alessandro Minoccheri Jul 03 '13 at 19:29
  • Ok I have seen that works, but the main problem now is how to construct the array in collection from my two json. is possible to integrate into your code two external json like in the other question to obtain collection like yours? Please help me – Alessandro Minoccheri Jul 03 '13 at 19:36
  • You can do hotels.fetch() to retrieve the JSON with nested models and it should work fine. Just be sure to set the URL on the collection prior to fetch. – Brian Lewis Jul 03 '13 at 19:36
  • I will add to the answer for the loading of separate JSON. – Brian Lewis Jul 03 '13 at 19:38
  • Wow this seems works fine! Last thing but the minus..if I pass combos to a template is possible to use in underscore this array right? – Alessandro Minoccheri Jul 03 '13 at 20:24
  • Yes, Underscore templates can work with Arrays or Collections. It'd be the difference between _.each(array,iterator) and collection.each(iterator). – Brian Lewis Jul 03 '13 at 20:27
  • The only thing that I can't understand is why not use a View.. where I can put my "el" element to use for my template? I thinked that a View is really necessary for this scope, and is necessary for rendere or event after I thinked – Alessandro Minoccheri Jul 03 '13 at 20:30
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/32846/discussion-between-moderndegree-and-alessandro-minoccheri) – Brian Lewis Jul 03 '13 at 20:43
  • there is a strange things but.. var combos = this.hotels.get(1).getCombinations(); with this function what I retriev? because If I do this and after call my template I print only rooms of the first hotel. How can i get a loop to cycle for each hotel and print inside my template? – Alessandro Minoccheri Jul 03 '13 at 22:41
  • this.hotels.each(function(hotel){var combos = hotel.getCombinations();}); – Brian Lewis Jul 03 '13 at 22:47
  • this.hotels.get(1) retrieves the hotel with id=1 – Brian Lewis Jul 03 '13 at 22:47
  • Have seen your update but return me always undefined... uhm.. if i try to delete hotel3 that it hasn't room is good your code – Alessandro Minoccheri Jul 03 '13 at 23:04
  • is very important: is possible to add a new argument to product cartesian like: return cartesian(_.values(this.rooms.groupBy('level'), _.values(this.rooms.groupBy('meals'))); please answer to me is very important – Alessandro Minoccheri Oct 10 '13 at 16:22
  • You should create a new question. It would also be helpful to know what you are trying to accomplish with the additional argument. – Brian Lewis Oct 11 '13 at 15:26
1

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
});
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Ok, I have understand a little bit and I have seen the function to do that. can you please complete the answer with the complete code (without the helpeer function)? is possible please? Thanks – Alessandro Minoccheri Jul 02 '13 at 15:15
  • If I put your code I have some problem, for this I need the "complete" solution – Alessandro Minoccheri Jul 02 '13 at 15:51
  • The forst problem is in the groupby because return me an array with: add, after... why? – Alessandro Minoccheri Jul 02 '13 at 15:55
  • The `rooms` should be an object `{1: {1: [r1, r1_3], 2:[r1_1], 3:[r1_4]}, 2: {1: [r2, r2_1]}}`. Did that work? I'm not sure to what variable (or property) you wanted to assign the combinations, so I couldn't put that in my code - could you lead me to this please? – Bergi Jul 02 '13 at 22:22
  • Ok, to understand my app see this two questions: http://stackoverflow.com/questions/17434649/backbone-how-to-construct-json-correctly http://stackoverflow.com/questions/17394461/backbone-insert-an-app-inside-an-app this is what I'm trying to do.. json of hotel parsed for each hotel I want to retrieve from another json its rooms and make the combinations. Could you please help me to this. Your code is really interest and if you help me to put it in right mode inside my app (see the answer of the second question is how is write now my app ) @Bergi – Alessandro Minoccheri Jul 03 '13 at 06:12
  • Here is a somewhat working plunkr of what @Bergi suggested - http://plnkr.co/edit/0Z53Np3fSXhDJdvcftEn?p=preview It still doesn't seem to give you the correct breakdown but maybe you can work it out. – Brian Lewis Jul 03 '13 at 15:37
  • @Bergi Thanks for the lesson in cartesian. I'm not sure it helped the OP but I learned something new today. I updated your answer with a working example. – Brian Lewis Jul 03 '13 at 17:06
  • I'd like @Bergi to get the credit, as I used his answer to figure it out. Here is a Plunkr to demonstrate the solution: http://plnkr.co/edit/NHE9V5?p=preview – Brian Lewis Jul 03 '13 at 17:14
  • 1
    ok if for you this works, can you please answer at the question with the right code? @moderndegree – Alessandro Minoccheri Jul 03 '13 at 17:18