使用函数式编程将数据从多个对象数组映射到 Javascript 中的新对象

Mapping data from multiple arrays of objects to a new object in Javascript using functional programming

我正在尝试创建一个需要映射的对象 来自函数中包含的四个数组的数据。目标 就是用函数式编程来完成新地图。 因此,例如,方法 map、reduce、forEach、concat 和 将允许过滤器以及自定义函数。

我已经找到了一个使用 for 循环的非功能性解决方案, 我在下面包括了。但是,我纯粹停留在 功能方法。

所需结果的数据集和数据可以是 在 http://jhusain.github.io/learnrx/ 上独立查看,但我在下面包含了数据。

objective 是使用函数式编程重新映射数据,使其类似于所需的输出(包含在下面)。

如果能帮助我使用函数式方法重新映射数据,我将不胜感激 并从下面显示的数据开始。

问题的确切文本指出:

Exercise 26: Converting from Arrays to Deeper Trees

Let's try creating a deeper tree structure. This time we have 4 seperate 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. In other words we want to build the following structure:

期望的输出是:

[
    {
        "name": "New Releases",
        "videos": [
            {
                "id": 65432445,
                "title": "The Chamber",
                "time": 32432,
                "boxart": "http://cdn-0.nflximg.com/images/2891/TheChamber130.jpg"
            },
            {
                "id": 675465,
                "title": "Fracture",
                "time": 3534543,
                "boxart": "http://cdn-0.nflximg.com/images/2891/Fracture120.jpg"
            }
        ]
    },
    {
        "name": "Thrillers",
        "videos": [
            {
                "id": 70111470,
                "title": "Die Hard",
                "time": 645243,
                "boxart": "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg"
            },
            {
                "id": 654356453,
                "title": "Bad Boys",
                "time": 984934,
                "boxart": "http://cdn-0.nflximg.com/images/2891/BadBoys140.jpg"
            }
        ]
    }
]

作为起点的函数如下。

我已将函数命名为 "combine" 以便我们可以调用它。另外,我有 包括我的解决方案,由 for 循环组成,在 书签数组后的函数。我仅尝试功能性解决方案 走到这一步:

return lists.map(function(list) {
        return {
            name: list.name,
            videos:
                videos.
                    filter(function(video) {
                        return video.listId === list.id;
                    }).    // I got stuck at this point.

这是一组起始数据:

function combine() {
    var 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 }
        ];

        //My non-functional solution

    sizeArr = [];

        for(var i = 0; i < lists.length; i++){
            lists[i].videos = [];

            for(var j = 0; j < videos.length; j++){
                if(videos[j].listId === lists[i].id){
                    lists[i].videos.push(videos[j]);

                }
            for(var k = 0; k < bookmarks.length; k++){
                if(bookmarks[k].videoId === videos[j].id && videos[j].listId === lists[i].id){
                    videos[j].time = bookmarks[k].time;
                }

            for(var l = 0; l < boxarts.length; l++){
                var size = boxarts[l].width * boxarts[l].height;
                sizeArr.push(size);
                sizeArr = sizeArr.sort(function(min, max){
                    if(min < max){
                        return min;
                    }

                     if(boxarts[l].videoId === videos[j].id && videos[j].listId === lists[i].id){
                        videos[j].boxart = boxarts[l].url;
                    } 

                });
            }

            }

            }
            delete lists[i].id;
        }

    return lists;
}
combine();

这可行,但效率不高。您首先可以从您的数组创建字典或映射以允许快速 id 查找。这样就不需要过滤器了。

我也避免改变初始数据结构。

 var 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
   }];


 var videosByList = lists.map(function(list) {
   return {
     name: list.name,
     videos: videos.filter(function(video) {
       return video.listId == list.id;
     }).map(function(video) {
       return {
         id: video.id,
         title: video.title,
         time: bookmarks.filter(function(bookmark) {
           return bookmark.videoId == video.id;
         }).pop().time,
         boxart: boxarts.filter(function(boxart) {
           return boxart.videoId == video.id;
         }).sort(function(a, b) {
           return a.url < b.url ? 1 : (a.url > b.url ? -1 : 0);
         }).pop().url
       };
     })
   };
 });


 document.querySelector('pre').appendChild(document.createTextNode(JSON.stringify(videosByList, null, 4)))
<pre></pre>

function combine () {

  var Genres = [];

  lists.forEach(function(genre) {
    var currGenre = makeGenre(genre.name, []);

    videos.filter(function(video){
      if (video.listId === genre.id) {
        return video;
      }
    }).forEach(function(video) {

      var time = bookmarks.filter(function(mark){
        if (video.id === mark.videoId) {
          return mark.time;
        }
      })[0];

      var art = boxarts.filter(function(art){
        if (video.id === art.videoId) {
          return art;
        }
      }).sort(function (a, b) { return a.width * a.height > b.width * b.height; })[0];

      currGenre.videos.push(makeVideo(video.id, video.title, time, art));
    });
    Genres.push(currGenre);
  });
  return Genres;
}

function makeGenre (name) {
  return {
    name: name,
    videos: []
  };
}

function makeVideo (id, title, time, boxart) {
  return {
    id: id,
    title: title,
    time: time,
    boxart: boxart
  };
}

OP 链接的练习页面中有一个 "Show Answer" 按钮。我猜答案按钮在 OP 选择的浏览器中不起作用,所以我将解决方案粘贴在这里,包括练习的额外背景信息:

return lists.map(function(list) {
  return {
    name: list.name,
    videos: 
      videos.
        filter(function(video) {
          return video.listId === list.id;
        }).
        concatMap(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 };
            });
      })
  };
});

这些练习建立在以前的知识之上,演示了 concatMap 函数(实现见练习 13),为方便起见粘贴在这里:

Array.prototype.concatMap = function(projectionFunctionThatReturnsArray) {
  return this.
    map(function(item) {
      return projectionFunctionThatReturnsArray(item);
    }).
    // apply the concatAll function to flatten the two-dimensional array
    concatAll();
};

concatAll 在练习 10 中定义,为方便起见粘贴在这里:

Array.prototype.concatAll = function() {
  var results = [];
  this.forEach(function(subArray) {
    results.push.apply(results, subArray);
  });

  return results;
};

练习说明还规定:"There's just more one thing: you can't use indexers. In other words, this is illegal var itemInArray = movieLists[0];"

编辑

来自 OP 的新信息。 OP 似乎想在没有练习页面上下文的情况下 运行 练习,并查看生成的答案以验证它是否正确。为此,您需要定义一些额外的函数,因为练习实现的版本与 JavaScript 中默认提供的版本略有不同(即 filtermapreduceconcatMapconcatAllzip)。如果您使用默认的 JavaScript 版本,那么结果将如 OP 所述丢失。

为了您的方便,我已经设置了所需的功能和解决方案,并将答案记录到 jsbin.com 中的控制台: http://jsbin.com/tolupe/edit?js,console