0

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. Question: I am trying to combine boxarts.filter().reduce() into one iteration boxarts.reduce() in other words integrate filter() into reduce() . Really important- using reduce() and not to change the rest of the code if it is possible. I am trying to understand the ins and outs of reduce() . Any one to help?

the part of the code I want to refactor:

[boxarts.filter(function (boxart) {
                            return boxart.videoId === video.id;
                        }).reduce(function (acc, curr) {

                            if (acc.width * acc.height < curr.width * curr.height) {
                                return acc
                            } else {
                                return curr
                            }

                            return acc
                        }, [])]

the whole code

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: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard300.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;
};


// Kick off the timer
console.time('filter.reduce');

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) {

                            if (acc.width * acc.height < curr.width * curr.height) {
                                return acc
                            } else {
                                return curr
                            }

                            return acc
                        }, [])],
                        function (bookmark, boxart) {
                            return { id: video.id, title: video.title, time: bookmark.time, boxart: boxart.url };
                        });
                })
    };
});

// End the timer, get the elapsed time
console.timeEnd('filter.reduce');

// //to enable deep level flatten use recursion with reduce and concat
let concatArr1 = (function flattenDeep(arr1) {
    return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
})(arr1);

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

Desired Output

enter image description here

SOLUTION

 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.reduce(function (acc, curr) {
                            if (curr.videoId === video.id) {
                                if (acc.width * acc.height < curr.width * curr.height) {
                                    return acc
                                } else {
                                    return curr
                                }
                            }

                            return acc
                        }, [])],
                        function (bookmark, boxart) {
                            return { id: video.id, title: video.title, time: bookmark.time, boxart: boxart.url };
                        });
                })
    };
});
2
  • There are multiple boxarts for one video id, which one you will consider in answer? Commented Jun 14, 2018 at 6:20
  • @amrendersingh with the smallest size = width * height Commented Jun 14, 2018 at 6:21

3 Answers 3

1

You can try the following, basically create a map of related properties using Array.reduce and than at last map the resultant map using Array.map :

const lists = [{"id":5434364,"name":"New Releases"},{"id":65456475,"name":"Thrillers"}];
const  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"}];
const 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":300,"height":200,"url":"http://cdn-0.nflximg.com/images/2891/DieHard300.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"}];
const bookmarks = [{"videoId":65432445,"time":32432},{"videoId":675465,"time":3534543},{"videoId":70111470,"time":645243},{"videoId":654356453,"time":984934}];
   
const bookmarksMap = bookmarks.reduce((a,obj)=>{
    a[obj.videoId] = {
      "time" : obj.time
    };
    return a;
},{});
const boxartsMap = boxarts.reduce((a,obj)=>{
    if(!a[obj.videoId] || a[obj.videoId].min > (obj.width*obj.height)){
       a[obj.videoId] = {
        "boxart" : obj.url,
         "min" : (obj.width*obj.height)
        } 
      } 
    return a;
},{});
    
const videosMap = videos.reduce((a,obj)=>{
  if(!a[obj.listId]){
    a[obj.listId] = [];
  }  
  a[obj.listId].push({
    "id" : obj.id,
    "title" : obj.title,
    "time" : bookmarksMap[obj.id].time,
    "boxart" : boxartsMap[obj.id].boxart
  });
    return a;
},{});
const result = lists.map((o)=>{
    var obj = {
    "name" : o.name, 
    "videos" : videosMap[o.id]
    };
    return obj;
 });
console.log(result);

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

3 Comments

unfortunatly this code produce mixed result of smallest and biggest size.
@JohnJohn i have updated the answer. It produces correct out now :-)
unfortunatly I can not accept it because I have already an working code. as an initial condition I requested not to change the rest of the code. but I will give one point up for trying.
0

You could get required result using following arrays method

  • reduce()

  • filter()

  • find()

  • map()

  • push()

Demo

let 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: 300,height: 200,url: "http://cdn-0.nflximg.com/images/2891/DieHard300.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}];

let res = lists.reduce((r, o) => {
    let filtrArr = videos.filter(({listId}) => listId == o.id);
  
    let result = filtrArr.map(item => {
        let find = boxarts.filter(({videoId}) => videoId == item.id);

        find = (find || []).sort((a, b) => a.width > b.width)[0];
        item.boxarts = find ? find.url : '';
        
      	find = bookmarks.find(({videoId}) => videoId == item.id);
        item.time = find.time || '';
        
        return item;
    });
  
    r.push({
        name: o.name,
        videos: result
    })
    return r;
}, [])

console.log(res)
.as-console-wrapper {max-height: 100% !important;top: 0;}

1 Comment

this is a valid code but like i was saying for studying reason I want to keep the original code and make minor changes to boxarts.filter().reduce() to make it one iteration less boxarts.reduce()
0

I bumped into this question years later.

You can move the filter condition into the reduce callback, so that if the filter condition is not true, you return the accumulator without change:

Change this:

boxarts.filter(function (boxart) {
    return boxart.videoId === video.id;
}).reduce(function (acc, curr) {
    if (acc.width * acc.height < curr.width * curr.height) {
        return acc;
    } else {
        return curr;
    }
}, [])

to this:

boxarts.reduce(function (acc, curr) {
    if (boxart.videoId !== video.id || acc.width * acc.height < curr.width * curr.height) {
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ opposite of filter condition
        return acc;
    } else {
        return curr;
    }
}, [])

Comments

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.