在javascript数组中,我们有一种称为slice的方法,该方法返回数组一部分的浅表副本。是否可以在js映射中执行相同操作。例如,我的应用程序中有一个映射,其中包含键值对形式的以下数据。
let dataSet = new Map<number, string>();
{1, 'Andy'},
{2, 'Jack'},
{3, 'Smith'},
{4, 'Dave'},
...
{99, 'Sam'}
是否有任何方法可以根据开始索引和结束索引对地图进行切片,并从原始地图返回切片的地图(或数组)的副本。我对map.forEach((value, key) => {})
有所了解,但据我所知,它将始终从零开始并遍历每个索引。处理大型数据集时效率不高。我想要一种类似的方法
getSlicedDataFromMap(startIndex, endIndex){
// Logic
}
getSlicedDataFromMap(10, 20);
// returns {10, 'Carl'}, {11, 'Jerry'}, {12, 'Steve'}, ... , {20, 'Robert'}
答案 0 :(得分:0)
我认为这是不可能的,如Mozilla JavaScript reference所述:
Map对象按插入顺序迭代其元素 – for ... of循环为每次迭代返回[key,value]数组。
也就是说,如果您的数据采用以下数组形式:
const myMap = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
您可以执行以下操作:
function getSlicedDataFromMap(theMap, startIndex, endIndex) {
const sliced = [];
let i;
for (i = startIndex; i <= endIndex; i++) {
sliced.push([ i, theMap.get(i) ]);
}
return sliced;
}
const result = getSlicedDataFromMap(myMap, 1, 2);
答案 1 :(得分:0)
我真的很喜欢@ nina-scholz的答案,我认为这是该问题的公认答案。
我还想为那些遇到此问题的人提供一些东西,以寻找一种基于任意键(而不只是整数序列)的Map
切片方法。
这个答案有点冗长,因为我想表达这段旅程。
我们想要什么样的界面?
让我们首先定义我们如何使用函数:
const result = between('one-string', 'another-string', themap);
这似乎很好。非字符串键呢?函数之类的东西呢?
const randomFunction = () => 'I am very random';
const result = between('one-string', randomFunction, themap);
我认为那很好。
实施
考虑到我们可能从函数式编程中获得的一些想法,让我们构建此接口的实现。
如果我们有能力fold
(reduce
)Map
呢?这似乎在这里很有用。
让我们实现fold
(您的实现可能会有所不同,并且性能会更高。我将以一种令人愉悦的方式将其写出来):
const fold = (fn, identity, [head, ...tail]) => {
const res = fn(identity, head);
return !tail.length ? res : fold(fn, res, tail);
};
这使我们能够递归地遍历地图中的值(您可以实现迭代版本,这太好了!)。
好的,所以我们有fold
函数,我们的计划是使用它来实现我们的between
函数。让我们讨论一下。
between()
这里是between
的实现,它从上方利用了我们的fold
函数。我们一直遍历每个键/值对,直到找到开始的匹配为止,并且继续在结果中添加项,直到找到我们的结束匹配为止。
const between = (start, end, data) => {
const result = fold(
(agg, [key, value]) =>
!agg.taking && key !== start
? agg
: {
taking: key !== end,
taken: [...agg.taken, [key, value]]
},
{ taking: false, taken: [] },
data
);
return new Map([...result.taken]);
};
太好了!我们已经实现了与上面定义的原始API匹配的解决方案:
const result = between('one-string', 'another-string', themap);
还有一点可以提高效率
由于我们选择使用递归算法来实现fold
,而我们的between
利用了fold
,因此我们的效率很低,因为我们的“循环”将继续即使我们不会在结果中添加任何其他项目,我们也找到了最终项目。
有没有一种方法可以缩短递归过程?有!它通过引入一种新的类型来工作
已减少
Reduced
是一种新类型,我们可以将其添加到组合中,这将使我们能够快捷地进行搜索,并在找到结果中需要的最后一项后结束操作。
让我们实现一个简单的Reduced
类型并将其添加到混合中,以提高性能。
const reduced = x => ({ x, reduced: true });
我们去了,应该去做。现在让我们使用它:
首先,我们需要告诉fold
来考虑某个值被完全减小的可能性,也就是说,该值属于我们新的Reduced
类型:
const fold = (fn, identity, [head, ...tail]) => {
const res = fn(identity, head);
if (res.reduced) { // <--- We've added this
return res.x;
}
return !tail.length ? res : fold(fn, res, tail);
};
太棒了!现在我们可以从折叠中返回一个Reduced
值,而fold
函数将尊重它,并早日退出。
让我们的between
函数将其传达给fold
:
const between = start => end => data => {
const result = fold(
(agg, [key, value]) => {
// v-- We've added a finished property and we return a Reduced type from fold
if (agg.finished) {
return reduced(agg);
}
return !agg.taking && key !== start
? agg
: {
finished: key === end,
taking: key !== end,
taken: [...agg.taken, [key, value]]
};
},
{ finished: false, taking: false, taken: [] },
data
).taken;
return new Map([...result]);
};
就在那里。现在,我们可以获取Map
中任何键之间的值,无论它们是整数,字符串,函数还是其他任何值。
以下是一些基于我们最终解决方案的代码框示例
答案 2 :(得分:0)
根据先前的答案,以下是TypeScript中的一些辅助函数,用于filter
和slice
和Map
,它们以更实用的方式构建:
// Map helper functions
function filterMap<K, V>(map: Map<K, V>, predicate: (item: [K, V], index: number) => boolean) {
return createMapFrom(map, items => items.filter(predicate));
}
function sliceMap<K, V>(map: Map<K, V>, startIndex?: number, endIndex?: number) {
return createMapFrom(map, items => items.slice(startIndex, endIndex));
}
function createMapFrom<K, V>(map: Map<K, V>, reducer: (items: [K, V][]) => [K, V][]) {
return new Map(reducer(Array.from(map)));
}
// Usage examples
const letterMap = new Map(
Array.from('abcdefghijklmopqrstuvwxyz')
.map((c, i) => [i + 1, c] as [number, string])); // Map(25) {1 => "a", 2 => "b", 3 => "c", 4 => "d", 5 => "e", …}
// Slicing
const twoFirstletterMap = sliceMap(letterMap, 0, 2); // Map(2) {1 => "a", 2 => "b"}
// Filtering
const vowels = 'aeiou';
const vowelMap = filterMap(letterMap, x => vowels.includes(x[1])); // Map(5) {1 => "a", 5 => "e", 9 => "i", 14 => "o", 20 => "u"}
const consonantMap = filterMap(letterMap, x => !vowels.includes(x[1])); // Map(20) {2 => "b", 3 => "c", 4 => "d", 6 => "f", 7 => "g", …}
希望有一天在JavaScript中使用管道|>
运算符,以获取createMapFrom
的更简洁的语法:
map
|> Array.from
|> reducer
|> Map.from