John John
John John

Reputation: 1455

Converting from Arrays to Deeper Trees using map, filter , reduce

We have 4 separate arrays each containing lists, videos, boxarts, and bookmarks respectively. Each object has a parent id, indicating its parent. We want to build an array of list objects, each with a name and a videos array. The videos array will contain the video's id, title, bookmark time, and smallest boxart url. Really important to use/debug Array.zip() as an initial requirement and keep the initial codebase. Anyone to help?

const lists = [
    {
        "id": 5434364,
        "name": "New Releases"
    },
    {
        "id": 65456475,
        name: "Thrillers"
    }
],
    videos = [
        {
            "listId": 5434364,
            "id": 65432445,
            "title": "The Chamber"
        },
        {
            "listId": 5434364,
            "id": 675465,
            "title": "Fracture"
        },
        {
            "listId": 65456475,
            "id": 70111470,
            "title": "Die Hard"
        },
        {
            "listId": 65456475,
            "id": 654356453,
            "title": "Bad Boys"
        }
    ],
    boxarts = [
        { videoId: 65432445, width: 130, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber130.jpg" },
        { videoId: 65432445, width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg" },
        { videoId: 675465, width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" },
        { videoId: 675465, width: 120, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture120.jpg" },
        { videoId: 675465, width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" },
        { videoId: 70111470, width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg" },
        { videoId: 70111470, width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg" },
        { videoId: 654356453, width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg" },
        { videoId: 654356453, width: 140, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys140.jpg" }
    ],
    bookmarks = [
        { videoId: 65432445, time: 32432 },
        { videoId: 675465, time: 3534543 },
        { videoId: 70111470, time: 645243 },
        { videoId: 654356453, time: 984934 }
    ];

Array.zip = function (bookmark, boxart, combinerFunction) {
    let counter,
        results = [];
   for (counter = 0; counter < Math.min(bookmark.length, boxart.length); counter++) {
        results.push(combinerFunction(bookmark[counter], boxart[counter]));
    }

    return results;
};    


let arr1 = lists.map(function (list) {
    return {
        name: list.name,
        videos:
            videos.
                filter(function (video) {
                    return video.listId === list.id;
                }).
                map(function (video) {
                    return Array.zip(
                        bookmarks.filter(function (bookmark) {
                            return bookmark.videoId === video.id;
                        }),
                        boxarts.filter(function (boxart) {
                            return boxart.videoId === video.id;
                        }).
                            reduce(function (acc, curr) {
                                return acc.width * acc.height < curr.width * curr.height ? acc : curr;
                            }),
                        function (bookmark, boxart) {
                            return { id: video.id, title: video.title, time: bookmark.time, boxart: boxart.url };
                        });
                })
    };
});

let concatArr = (function flattenDeep(arr1) {
    return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
})(arr1);

console.log(concatArr)

Result

enter image description here

Desired Output

enter image description here

SOLUTION

Array.zip = function (bookmark, boxart, combinerFunction) {
    let counter,
        results = [];
    debugger
    for (counter = 0; counter < Math.min(bookmark.length, boxart.length); counter++) {
        results.push(combinerFunction(bookmark[counter], boxart[counter]));
    }

    return results;
};    


let arr1 = lists.map(function (list) {
    return {
        name: list.name,
        videos:
            videos.
                filter(function (video) {
                    return video.listId === list.id;
                }).
                map(function (video) {
                    return Array.zip(
                        bookmarks.filter(function (bookmark) {
                            return bookmark.videoId === video.id;
                        }),
                        boxarts.filter(function (boxart) {
                            return boxart.videoId === video.id;
                        }).
                            reduce(function (acc, curr) {
                                return [acc.width * acc.height < curr.width * curr.height ? acc : curr];
                            }),
                        function (bookmark, boxart) {
                            return { id: video.id, title: video.title, time: bookmark.time, boxart: boxart.url };
                        });
                })
    };
});

    let concatArr = (function flattenDeep(arr1) {
        return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
    })(arr1);

console.log(JSON.stringify(concatArr, null, 2))

SOLUTION Output

enter image description here

enter image description here

Upvotes: 1

Views: 118

Answers (3)

Vitiok
Vitiok

Reputation: 84

Since you had as requirement to use zip. This solution uses lodash _.zipWith. Solution is not as optimal as the once with reduce.

    let result = _.zipWith(lists, videos, boxarts, bookmarks, function(list, video, boxart, bookmark) {
      if (list === undefined) return;
      return {
        name: list.name,
        videos: videos.filter(item => item.listId === list.id).map(item => {
          return {
            id: item.id,
            title: item.title,
            boxart: boxarts.find(boxart => boxart.videoId === item.id).url,
            time: bookmarks.find(bookmark => bookmark.videoId === item.id).time
          }
        })
      }
    }).filter(item => item !== undefined) 


    console.log(result[0]);
    console.log(result[1]);

Upvotes: 0

brk
brk

Reputation: 50291

Use map and find to find an object from array using a specific key.

The below snippet contains comments, hopefully that will be useful

const lists = [{
      "id": 5434364,
      "name": "New Releases"
    },
    {
      "id": 65456475,
      name: "Thrillers"
    }
  ],
  videos = [{
      "listId": 5434364,
      "id": 65432445,
      "title": "The Chamber"
    },
    {
      "listId": 5434364,
      "id": 675465,
      "title": "Fracture"
    },
    {
      "listId": 65456475,
      "id": 70111470,
      "title": "Die Hard"
    },
    {
      "listId": 65456475,
      "id": 654356453,
      "title": "Bad Boys"
    }
  ],
  boxarts = [{
      videoId: 65432445,
      width: 130,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/TheChamber130.jpg"
    },
    {
      videoId: 65432445,
      width: 200,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg"
    },
    {
      videoId: 675465,
      width: 200,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg"
    },
    {
      videoId: 675465,
      width: 120,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/Fracture120.jpg"
    },
    {
      videoId: 675465,
      width: 300,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg"
    },
    {
      videoId: 70111470,
      width: 150,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg"
    },
    {
      videoId: 70111470,
      width: 200,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg"
    },
    {
      videoId: 654356453,
      width: 200,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg"
    },
    {
      videoId: 654356453,
      width: 140,
      height: 200,
      url: "http://cdn-0.nflximg.com/images/2891/BadBoys140.jpg"
    }
  ],
  bookmarks = [{
      videoId: 65432445,
      time: 32432
    },
    {
      videoId: 675465,
      time: 3534543
    },
    {
      videoId: 70111470,
      time: 645243
    },
    {
      videoId: 654356453,
      time: 984934
    }
  ];
// using map function create an array of objects having name and videos as key. The name key will be taken from lists array and using the id from list array filter out the corrosponding objects from videos array
var newCol = lists.map(function(item) {
  return {
    names: item.name,
    videos: videos.filter(function(videoItem) {
      return item.id === videoItem.listId
    })
  }
})

// now iterate the newly created array and using its videoId , find the corrosponding objects from the boxart and bookmarks array
newCol.forEach(function(item) {
  item.videos.forEach(function(items, index) {
    let getUrl = boxarts.find(function(vidId) {
      return vidId.videoId === items.id
    })
    // update the object by adding new key videoId & time
    item.videos[index].boxarts = getUrl.url;
    let getTime = bookmarks.find(function(vidId) {
      return vidId.videoId === items.id
    });
    item.videos[index].time = getTime.time;

  })

})
console.log(newCol)

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370729

Runtime complexity can be greatly simplified - just reduce the times and boxarts into an object indexed by video ID first, and then you can do the same thing for the videos indexed by the list ID:

const lists=[{"id":5434364,"name":"New Releases"},{"id":65456475,name:"Thrillers"}],videos=[{"listId":5434364,"id":65432445,"title":"The Chamber"},{"listId":5434364,"id":675465,"title":"Fracture"},{"listId":65456475,"id":70111470,"title":"Die Hard"},{"listId":65456475,"id":654356453,"title":"Bad Boys"}],boxarts=[{videoId:65432445,width:130,height:200,url:"http://cdn-0.nflximg.com/images/2891/TheChamber130.jpg"},{videoId:65432445,width:200,height:200,url:"http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg"},{videoId:675465,width:200,height:200,url:"http://cdn-0.nflximg.com/images/2891/Fracture200.jpg"},{videoId:675465,width:120,height:200,url:"http://cdn-0.nflximg.com/images/2891/Fracture120.jpg"},{videoId:675465,width:300,height:200,url:"http://cdn-0.nflximg.com/images/2891/Fracture300.jpg"},{videoId:70111470,width:150,height:200,url:"http://cdn-0.nflximg.com/images/2891/DieHard150.jpg"},{videoId:70111470,width:200,height:200,url:"http://cdn-0.nflximg.com/images/2891/DieHard200.jpg"},{videoId:654356453,width:200,height:200,url:"http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg"},{videoId:654356453,width:140,height:200,url:"http://cdn-0.nflximg.com/images/2891/BadBoys140.jpg"}],bookmarks=[{videoId:65432445,time:32432},{videoId:675465,time:3534543},{videoId:70111470,time:645243},{videoId:654356453,time:984934}];

const timeById = bookmarks.reduce((a, { videoId, time }) => {
  a[videoId] = time;
  return a;
}, {});
const boxartById = boxarts.reduce((a, { videoId, url }) => {
  a[videoId] = url;
  return a;
}, {});

const videosByListId = videos.reduce((a, { listId, id, title }) => {
  if (!a[listId]) a[listId] = [];
  a[listId].push({
    id,
    title,
    time: timeById[id],
    boxart: boxartById[id],
  });
  return a;
}, {});

const output = lists.map(({ id, name }) => ({
  name,
  videos: videosByListId[id],
}));
console.log(output);

Upvotes: 1

Related Questions