使用函数式编程将数据从多个对象数组映射到 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 中默认提供的版本略有不同(即 filter
、map
、reduce
、concatMap
、concatAll
、zip
)。如果您使用默认的 JavaScript 版本,那么结果将如 OP 所述丢失。
为了您的方便,我已经设置了所需的功能和解决方案,并将答案记录到 jsbin.com 中的控制台:
http://jsbin.com/tolupe/edit?js,console
我正在尝试创建一个需要映射的对象 来自函数中包含的四个数组的数据。目标 就是用函数式编程来完成新地图。 因此,例如,方法 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 中默认提供的版本略有不同(即 filter
、map
、reduce
、concatMap
、concatAll
、zip
)。如果您使用默认的 JavaScript 版本,那么结果将如 OP 所述丢失。
为了您的方便,我已经设置了所需的功能和解决方案,并将答案记录到 jsbin.com 中的控制台: http://jsbin.com/tolupe/edit?js,console