Using Flatmap map and filter instead of loop

Shmuel Niraev Source

Consider the movie list

let movieLists = [{
    name: "Instant Queue",
    videos: [{
        "id": 70111470,
        "title": "Die Hard",
        "boxarts": [{
            width: 150,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg"
          },
          {
            width: 200,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg"
          }
        ],
        "url": "http://api.netflix.com/catalog/titles/movies/70111470",
        "rating": 4.0,
        "bookmark": []
      },
      {
        "id": 654356453,
        "title": "Bad Boys",
        "boxarts": [{
            width: 200,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg"
          },
          {
            width: 150,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/BadBoys150.jpg"
          }

        ],
        "url": "http://api.netflix.com/catalog/titles/movies/70111470",
        "rating": 5.0,
        "bookmark": [{
          id: 432534,
          time: 65876586
        }]
      }
    ]
  },
  {
    name: "New Releases",
    videos: [{
        "id": 65432445,
        "title": "The Chamber",
        "boxarts": [{
            width: 150,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/TheChamber150.jpg"
          },
          {
            width: 200,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg"
          }
        ],
        "url": "http://api.netflix.com/catalog/titles/movies/70111470",
        "rating": 4.0,
        "bookmark": []
      },
      {
        "id": 675465,
        "title": "Fracture",
        "boxarts": [{
            width: 200,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg"
          },
          {
            width: 150,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg"
          },
          {
            width: 300,
            height: 200,
            url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg"
          }
        ],
        "url": "http://api.netflix.com/catalog/titles/movies/70111470",
        "rating": 5.0,
        "bookmark": [{
          id: 432534,
          time: 65876586
        }]
      }
    ]
  }
]

The movieList consist from those interfaces

interface Video {
  id:number;
  title:string;
  boxarts:{width:number,  height:number, url: string}[];
  uri:string;
  rating:number;
  bookmark: {id:number, time:number}[];
}

interface VideoCategory {
  name:string;
  videos: Video[];
} 

I want to Implement a function getBoxArts() which gets a type similar to movieLists and returns a map {id, title, boxart} for every video in the movieLists, such that the boxart property in the result will be the url of the boxart object with dimensions of 150x200px.I need to use only Flatmap map and filter. But the only way I cant think of is indexed like this:

function getBoxarts(movie) {
  let res = [];
  for (let i = 0; i < movie.length; i++) {
    for (let j = 0; j < movie[i].videos.length; j++) {
      for (let l = 0; l < movie[i].videos[j].boxarts.length; l++) {
        if ((movie[i].videos[j].boxarts[l].width == 150) && (movie[i].videos[j].boxarts[l].height == 200)) {
          res.push({
            id: movie[i].videos[j].id,
            title: movie[i].videos[j].title,
            boxarts: movie[i].videos[j].boxarts[l]
          });
        }
      }

    }

  }
  return res;
}

The code contains 3 loops and it is not pretty.

edit: I have defined a function called a flatmap

    const concat = (x,y) =>
         x.concat(y)

    const flatmap = (f,xs) =>
         xs.map(f).reduce(concat, [])

What it does is gets a function f and an array A as parameter, and returns the concatenation of all the arrays returned by f when it is applied to each element in A. for example

     Flatmap((x)=>x[0], [[[1,2], [3,4]], [[5,6], [7,8]]]) => [1,2,5,6]
javascripttypescript

Answers

answered 6 months ago Hassan Imam #1

You can use functional style and use array#reduce, array#forEach and array#find to filter out boxart with width 150 and height 200.

let movieLists = [{ name: "Instant Queue", videos: [{ "id": 70111470, "title": "Die Hard", "boxarts": [{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg" }, { width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg"} ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 4.0, "bookmark": [] }, { "id": 654356453, "title": "Bad Boys", "boxarts": [{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg" }, { width:150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys150.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 5.0, "bookmark": [{ id: 432534, time: 65876586 }] } ] }, { name: "New Releases", videos: [{ "id":65432445, "title": "The Chamber", "boxarts": [{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber150.jpg" }, { width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470","rating": 4.0, "bookmark": [] }, { "id": 675465, "title": "Fracture", "boxarts": [{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" }, { width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg"}, { width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 5.0, "bookmark": [{ id: 432534, time: 65876586 }] } ] } ],
    result = movieLists.reduce((r, {videos}) => {
      videos.forEach(({id, title, boxarts}) => {
        let boxart = boxarts.find(({width, height}) => width === 150 && height === 200);
        if(boxart) {
          r.push({id, title, boxarts : boxart});
        }
      });
      return r;
    }, []);
console.log(result);

You can first flatten your array, then search for the desired boxart and push it in your result array.

let movieLists = [{ name: "Instant Queue", videos: [{ "id": 70111470, "title": "Die Hard", "boxarts": [{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg" }, { width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg"} ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 4.0, "bookmark": [] }, { "id": 654356453, "title": "Bad Boys", "boxarts": [{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg" }, { width:150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys150.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 5.0, "bookmark": [{ id: 432534, time: 65876586 }] } ] }, { name: "New Releases", videos: [{ "id":65432445, "title": "The Chamber", "boxarts": [{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber150.jpg" }, { width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470","rating": 4.0, "bookmark": [] }, { "id": 675465, "title": "Fracture", "boxarts": [{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" }, { width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg"}, { width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 5.0, "bookmark": [{ id: 432534, time: 65876586 }] } ] } ],
    result = [].concat(...movieLists.map(({videos}) => videos))
               .reduce((r,{id,title, boxarts}) => {
                let boxart = boxarts.find(({width, height}) => width === 150 && height === 200);
                if(boxart)
                  r.push({id, title, boxarts: boxart});
                return r;
              }, [])
console.log(result);

let movieLists = [{ name: "Instant Queue", videos: [{ "id": 70111470, "title": "Die Hard", "boxarts": [{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg" }, { width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg"} ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 4.0, "bookmark": [] }, { "id": 654356453, "title": "Bad Boys", "boxarts": [{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg" }, { width:150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys150.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 5.0, "bookmark": [{ id: 432534, time: 65876586 }] } ] }, { name: "New Releases", videos: [{ "id":65432445, "title": "The Chamber", "boxarts": [{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber150.jpg" }, { width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470","rating": 4.0, "bookmark": [] }, { "id": 675465, "title": "Fracture", "boxarts": [{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" }, { width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg"}, { width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" } ], "url": "http://api.netflix.com/catalog/titles/movies/70111470", "rating": 5.0, "bookmark": [{ id: 432534, time: 65876586 }] } ] } ],
    concat = (x,y) => x.concat(y),
    flatmap = (f,xs) => xs.map(f).reduce(concat, []),
    result = flatmap((({videos}) => videos), movieLists)
            .map(({id,title, boxarts}) => {
                let boxart = boxarts.filter(({width, height}) => width === 150 && height === 200);
                if(boxart || boxart.length)
                  return ({id, title, boxarts: boxart[0]});
                else
                  return undefined;
              })
              .filter(o => o)
console.log(result);

comments powered by Disqus