如何在 JavaScript 中用函数式编程从一对多列表构建倒置映射?
How to build an inverted map from a one-to-many list with functional programming in JavaScript?
澄清一下我所说的 "inverted map":
const foo =
{ "a": 10
, "b": 20
};
const foo_inverted =
{ "10": "a"
, "20": "b"
};
我有这个代表文件的对象:
const file =
{ id: 100
, tags: [20, 30]
};
给定一个文件列表,我需要构建一个地图,使我能够找到具有给定标签的所有文件。
来自这里:
const files =
[ { id: 100
, tags: [20, 30]
}
, { id: 200
, tags: [20, 40]
}
];
对此:
{ "20": { "100": 1, "200": 1 }
, "30": { "100": 1 }
, "40": { "200": 1 }
}
我最终得到了完成这项工作的代码:
const tag_file = (tag_id, file_id) => ({[tag_id]: {[file_id]: 1}});
const mergeDeepAll = reduce(mergeDeepRight, {});
const tag_map = compose(mergeDeepAll, lift(tag_file));
const tags_map = compose(mergeDeepAll, map(({id, tags}) => tag_map(tags, [id])));
tags_map(files);
//=> { "20": { "100": 1, "200": 1 }
//=> , "30": { "100": 1 }
//=> , "40": { "200": 1 }
//=> }
问题:我是否遗漏了任何可以让我更好地表达这一点的函数式编程概念?
不确定为什么需要 ramda,可以使用 reduce 和 forEach
const files = [{
id: 100,
tags: [20, 30]
}, {
id: 200,
tags: [20, 40]
}];
// loop over the array to make an object
const result = files.reduce((obj, file) => {
// loop over the tags
file.tags.forEach(
tag =>
obj[tag] ? // have we seen the tag?
obj[tag].push(file.id) : // yes
obj[tag] = [file.id] // no
)
return obj // return the object for reduce
}, {})
console.log(result)
编辑后
const files = [{
id: 100,
tags: [20, 30]
}, {
id: 200,
tags: [20, 40]
}];
// loop over the array to make an object
const result = files.reduce((obj, file) => {
// loop over the tags
file.tags.forEach(
tag => {
obj[tag] = obj[tag] || {} // have we seen the tag?
obj[tag][file.id] = 1 //
})
return obj // return the object for reduce
}, {})
console.log(result)
创建一个函数,使用 Array.map()
(idByTags
) 为每个对象生成对 [tag, id]
。使用 R.chain 将所有对象转换为此类对并将它们展平。按标签分组(R.head),然后映射对象(R.mapObjIndexed)并按 id 计数(R.last):
const { pipe, chain, groupBy, head, mapObjIndexed, countBy, last } = R
const idByTags = ({ id, tags }) => tags.map(tag => [tag, id])
const fn = pipe(
chain(idByTags),
groupBy(head),
mapObjIndexed(countBy(last))
)
const files = [{"id":100,"tags":[20,30]},{"id":200,"tags":[20,40]}]
const result = fn(files)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
澄清一下我所说的 "inverted map":
const foo =
{ "a": 10
, "b": 20
};
const foo_inverted =
{ "10": "a"
, "20": "b"
};
我有这个代表文件的对象:
const file =
{ id: 100
, tags: [20, 30]
};
给定一个文件列表,我需要构建一个地图,使我能够找到具有给定标签的所有文件。
来自这里:
const files =
[ { id: 100
, tags: [20, 30]
}
, { id: 200
, tags: [20, 40]
}
];
对此:
{ "20": { "100": 1, "200": 1 }
, "30": { "100": 1 }
, "40": { "200": 1 }
}
我最终得到了完成这项工作的代码:
const tag_file = (tag_id, file_id) => ({[tag_id]: {[file_id]: 1}});
const mergeDeepAll = reduce(mergeDeepRight, {});
const tag_map = compose(mergeDeepAll, lift(tag_file));
const tags_map = compose(mergeDeepAll, map(({id, tags}) => tag_map(tags, [id])));
tags_map(files);
//=> { "20": { "100": 1, "200": 1 }
//=> , "30": { "100": 1 }
//=> , "40": { "200": 1 }
//=> }
问题:我是否遗漏了任何可以让我更好地表达这一点的函数式编程概念?
不确定为什么需要 ramda,可以使用 reduce 和 forEach
const files = [{
id: 100,
tags: [20, 30]
}, {
id: 200,
tags: [20, 40]
}];
// loop over the array to make an object
const result = files.reduce((obj, file) => {
// loop over the tags
file.tags.forEach(
tag =>
obj[tag] ? // have we seen the tag?
obj[tag].push(file.id) : // yes
obj[tag] = [file.id] // no
)
return obj // return the object for reduce
}, {})
console.log(result)
编辑后
const files = [{
id: 100,
tags: [20, 30]
}, {
id: 200,
tags: [20, 40]
}];
// loop over the array to make an object
const result = files.reduce((obj, file) => {
// loop over the tags
file.tags.forEach(
tag => {
obj[tag] = obj[tag] || {} // have we seen the tag?
obj[tag][file.id] = 1 //
})
return obj // return the object for reduce
}, {})
console.log(result)
创建一个函数,使用 Array.map()
(idByTags
) 为每个对象生成对 [tag, id]
。使用 R.chain 将所有对象转换为此类对并将它们展平。按标签分组(R.head),然后映射对象(R.mapObjIndexed)并按 id 计数(R.last):
const { pipe, chain, groupBy, head, mapObjIndexed, countBy, last } = R
const idByTags = ({ id, tags }) => tags.map(tag => [tag, id])
const fn = pipe(
chain(idByTags),
groupBy(head),
mapObjIndexed(countBy(last))
)
const files = [{"id":100,"tags":[20,30]},{"id":200,"tags":[20,40]}]
const result = fn(files)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>