-2

I need help merging two objects in AngularJS :)

I'm using the trakt.tv API (http://docs.trakt.apiary.io/) to pull in history data. This returns a list of movies and episodes the user has watched (see: http://docs.trakt.apiary.io/#reference/sync/get-history)

As you can see it does not hold the users rating for a specific movie or episode. But there is a way to get all the users ratings for movies and tv shows ect.. (see http://docs.trakt.apiary.io/#reference/sync/get-ratings)

So what I want to do is match the users movie/episode ratings with the movie/episode in the history list, but I just can't wrap my head around how it should be done.

Example "History" object:

[
  {
    "id": 2008588422,
    "watched_at": "2016-05-17T10:36:12.000Z",
    "action": "watch",
    "type": "movie",
    "movie": {
      "title": "Batman v Superman: Dawn of Justice",
      "year": 2016,
      "ids": {
        "trakt": 129583,
        "slug": "batman-v-superman-dawn-of-justice-2016",
        "imdb": "tt2975590",
        "tmdb": 209112
      }
    }
  },
  {
    "id": 1995814508,
    "watched_at": "2016-05-09T22:39:47.000Z",
    "action": "checkin",
    "type": "movie",
    "movie": {
      "title": "Dirty Grandpa",
      "year": 2016,
      "ids": {
        "trakt": 188691,
        "slug": "dirty-grandpa-2016",
        "imdb": "tt1860213",
        "tmdb": 291870
      }
    }
  },
  {
    "id": 2005359787,
    "watched_at": "2016-05-09T01:00:00.000Z",
    "action": "watch",
    "type": "episode",
    "episode": {
      "season": 6,
      "number": 3,
      "title": "Oathbreaker",
      "ids": {
        "trakt": 1989021,
        "tvdb": 5579003,
        "imdb": "tt4131606",
        "tmdb": 1186952,
        "tvrage": 1065908650
      }
    },
    "show": {
      "title": "Game of Thrones",
      "year": 2011,
      "ids": {
        "trakt": 1390,
        "slug": "game-of-thrones",
        "tvdb": 121361,
        "imdb": "tt0944947",
        "tmdb": 1399,
        "tvrage": 24493
      }
    }
  }
]

Example "Ratings" object:

[
  {
    "rated_at": "2016-05-17T10:36:28.000Z",
    "rating": 7,
    "type": "movie",
    "movie": {
      "title": "Batman v Superman: Dawn of Justice",
      "year": 2016,
      "ids": {
        "trakt": 129583,
        "slug": "batman-v-superman-dawn-of-justice-2016",
        "imdb": "tt2975590",
        "tmdb": 209112
      }
    }
  },
  {
    "rated_at": "2016-04-05T15:55:36.000Z",
    "rating": 8,
    "type": "movie",
    "movie": {
      "title": "You Don't Mess With the Zohan",
      "year": 2008,
      "ids": {
        "trakt": 5835,
        "slug": "you-don-t-mess-with-the-zohan-2008",
        "imdb": "tt0960144",
        "tmdb": 10661
      }
    }
  },
  {
    "rated_at": "2016-05-24T16:19:54.000Z",
    "rating": 8,
    "type": "episode",
    "episode": {
      "season": 6,
      "number": 3,
      "title": "Oathbreaker",
      "ids": {
        "trakt": 1989021,
        "tvdb": 5579003,
        "imdb": "tt4131606",
        "tmdb": 1186952,
        "tvrage": 1065908650
      }
    },
    "show": {
      "title": "Game of Thrones",
      "year": 2011,
      "ids": {
        "trakt": 1390,
        "slug": "game-of-thrones",
        "tvdb": 121361,
        "imdb": "tt0944947",
        "tmdb": 1399,
        "tvrage": 24493
      }
    }
  }
]

Wanted result:

[
  {
    "id": 2008588422,
    "rated_at": "2016-05-17T10:36:28.000Z",
    "rating": 7,
    "watched_at": "2016-05-17T10:36:12.000Z",
    "action": "watch",
    "type": "movie",
    "movie": {
      "title": "Batman v Superman: Dawn of Justice",
      "year": 2016,
      "ids": {
        "trakt": 129583,
        "slug": "batman-v-superman-dawn-of-justice-2016",
        "imdb": "tt2975590",
        "tmdb": 209112
      }
    }
  },
  {
    "id": 1995814508,
    "watched_at": "2016-05-09T22:39:47.000Z",
    "action": "checkin",
    "type": "movie",
    "movie": {
      "title": "Dirty Grandpa",
      "year": 2016,
      "ids": {
        "trakt": 188691,
        "slug": "dirty-grandpa-2016",
        "imdb": "tt1860213",
        "tmdb": 291870
      }
    }
  },
  {
    "id": 2005359787,
    "rated_at": "2016-05-24T16:19:54.000Z",
    "rating": 8,
    "watched_at": "2016-05-09T01:00:00.000Z",
    "action": "watch",
    "type": "episode",
    "episode": {
      "season": 6,
      "number": 3,
      "title": "Oathbreaker",
      "ids": {
        "trakt": 1989021,
        "tvdb": 5579003,
        "imdb": "tt4131606",
        "tmdb": 1186952,
        "tvrage": 1065908650
      }
    },
    "show": {
      "title": "Game of Thrones",
      "year": 2011,
      "ids": {
        "trakt": 1390,
        "slug": "game-of-thrones",
        "tvdb": 121361,
        "imdb": "tt0944947",
        "tmdb": 1399,
        "tvrage": 24493
      }
    }
  }
]

Basically rating data should be added to the corresponding movies and episodes inside the history object.

An angular.merge or .extend doesn't give the desired result, and these seem to be very basic looking at the docs (https://docs.angularjs.org/api/ng/function/angular.merge)

All help welcome! :)

Thanks

11
  • what does angular.merge give you? Would angular.extend do the job? Commented May 24, 2016 at 16:46
  • Hi @bmartin angular.merge returns: pastebin.com/jkaDnfGS and angular.extend gives: pastebin.com/K3pZ9RJd both not the desired result :( Commented May 24, 2016 at 16:51
  • angular.merge looks correct to me? Commented May 24, 2016 at 16:52
  • It's not, ratings should only be added to movies and episodes that exist in the History object. As you can see in the merge result ( pastebin.com/jkaDnfGS) the movie object for "Dirty Grandpa" became "You Don't Mess With the Zohan" (the rating data has simply overwritten the history object). Commented May 24, 2016 at 16:57
  • Also note this is just a sample, the real data will be a list of possibly hundreds of movies and episodes that have been rated, in random order. Same thing for the history data, in my sample "Batman v Superman: Dawn of Justice" is always the first movie but that might not be the case. I need some kind of merge that can recognize a specific movie (in the history object) and only if it has rating data for that specific movie, add that rating data correctly to the movie object. Commented May 24, 2016 at 17:01

1 Answer 1

1

After the op's comment where ES6 isn't an option ive created an ES5 equivalent with a find helper method to mimic ES6's find.

var merged = history.map(function (h) {
  var x = find(ratings, function (r) {
    return comparator(h, r);
  });

  return x ? angular.merge(h, x) : h;
});

/*
    Utility fn to get array.find style behaviour
*/
function find(arr, fn) {
  if (!Array.isArray(arr))
    return undefined;

  for(var i = 0; i < arr.length; i++){
    if (fn(arr[i]))
        return arr[i];
  }
}

fiddle


In my comment i said you could use ES5's array.some however i lied. some will return true should a single match meet your criteria (good for filters) however we want the actual object.

Luckily ES6 saves us yet again with find; (you could use filter in ES5 but this gives us an array back)

So with that something like this should be quite easy.

let merged = history.map(h => {
        let x =  ratings.find(r => comparator(r, h));
        return x ? angular.merge(h, x) : h;
    });

function comparator(review, history) {
    return review[review.type].ids.trakt === history[history.type].ids.trakt;
}

As always this is ES6 so IE might as well not turn up. You can run your ES6 through things like babel to transform into into ES5, or doe all the above with various for loops.

Fiddle

Sign up to request clarification or add additional context in comments.

5 Comments

Hi @ste2425, thanks for this detailed answer! This almost does what I want to accomplish, in the final outcome the object should have the history data with the rating data added to items when available. As you noted your outcome also includes a movie that has only been rated. But shouldn't show up in the outcome, because it is not in the users History. Also kangax.github.io/compat-table/es6 shows me ES6 isn't supported on mobile devices (android). I'm building an Ionic (ionicframework.com) mobile app so that's an issue.
Ahh so you want to flip, so history is the master and has rating info should it be available. That's quite easy. I would strongly advise trying to implement ES6 compliant code and using a tool such as babel to convert into ES5. Then once browsers catch up simply turn of the conversion. Ill edit the answer.
Yes that is correct! Ok, looking forward to your updated answer :)
@webslash Updated, hopefully will be of help
Absolutely brilliant, works as requested. Thank you very much @ste2425 !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.