问题描述
我是 JavaScript 编程的新手,我有一个场景如下 -
我有一个元素数组列表的输入 --> [abc,xyz,123] 并且输出将在数组对象中如下所示
[
{
"label" : "positive","id" : "abc"
},{
"label" : "positive","id" : "xyz"
},{
"label" : "negative",{
"label" : "positive and negative",{
"label" : "neg","id" : "123"
},"id" : "xyz"
}
]
现在我必须根据输入序列对输出进行排序 -
这里 abc 是第一个元素 xyz 是第二个元素 123是第三个元素
所以我的输出应该是这样的,所有的 id="abc" 应该先是,然后是 "xyz" 对象,然后是 "123" 对象
[
{
"label" : "positive","id" : "123"
}
]
关于我们如何实现这一目标的任何建议,请
解决方法
您可以首先使用 Map(一种类似对象的数据结构,可让您存储键值对)通过 id
对您的对象数组进行分组。键将是 id,而值将是一个以该键作为其 ID 的对象数组。将所有对象分组到 Map 后,您可以在数组 [abc,xyz,123]
上使用 .flatMap()
。首先创建 Map(而不是为数组中的每个元素搜索项目)允许您保持线性:
const target = ['abc','xyz','123'];
const arr = [{ "label": "positive","id": "abc" },{ "label": "positive","id": "xyz" },{ "label": "negative",{ "label": "positive and negative",{ "label": "neg","id": "123" },"id": "xyz" } ];
const grouped = arr.reduce((acc,obj) => {
const seen = acc.get(obj.id) || []; // get the seen array,if it doesn't exist,default it []
return acc.set(obj.id,seen.concat(obj)); // add the current object to its seen array,and update that Map
},new Map);
const res = target.flatMap(id => grouped.get(id)); // convert every value in arr to it's corresponding array from the Map held at `id`
console.log(res);
如果您不习惯使用 Maps、reduce 和 flatMap,您可能会发现以下方法更容易理解。它采用与上述相同的逻辑,只是以一种更命令的方式:
const target = ['abc',"id": "xyz" } ];
const grouped = {};
for(const obj of arr) {
const seen = grouped[obj.id] || [];
grouped[obj.id] = seen.concat(obj);
}
const res = [];
for(const id of target) {
res.push(...grouped[id]);
}
console.log(res);
以上通过创建一个名为 grouped
的对象来工作。这个想法是将 id
中每个对象的 arr
值存储为该对象内的键。一个对象不能有重复的键,所以如果我们遇到一个来自 arr
的对象,它的 id
已经是对象中的一个键,我们可以将该对象作为值添加到保存在钥匙。如果来自 id
的对象的 arr
没有出现在 grouped
中,我们可以添加它,并将其值设置为包含当前对象的数组。这个想法是创建一个这种形式的对象:
{
"123": [ { "label": "neg","id": "123" } ],"abc": [ { "label": "positive","id": "abc" } ],"xyz": [ { "label": "positive","id": "xyz" } ]
}
在构建上面的 grouped
对象时,这一行检查上面分组的对象是否已经包含当前对象 id,如果是,则将 seen
设置为该键保存的数组,否则,它设置为空数组:
const seen = grouped[obj.id] || [];
然后我们更新 seen
数组和 grouped
对象以添加当前对象:
grouped[obj.id] = seen.concat(obj);
既然一切都已分组,我们可以使用此对象来获取与使用 id
的特定 grouped[id]
关联的对象。在这种情况下,我们希望将 id
中的每个 target
转换为其关联的对象。这可以通过使用 target
循环遍历 for...of
数组中的 id,然后使用 grouped
对象获取与该 id 关联的对象来完成。然后,我们可以使用以下命令将我们从 grouped
获得的数组中的所有元素推入 res
数组:
res.push(...grouped[id]);
这会将 grouped[id]
返回的数组中的每个元素推送到 res
数组中。这与使用 res.push(grouped[id])
不同,因为这会将整个分组数组(不仅仅是元素)推入 res
数组。通过使用 spread syntax (...
),我们将分组数组中的元素作为 individual arguments 传递给 .push()
,允许我们只推送元素。
你可以这样做。
我们首先从我们需要的序列 {abc: 0,xyz: 1,123: 2}
创建一个像 ["abc","xyz","123"]
这样的优先级映射,然后使用这个映射对数组中的项目进行排序
const data = [{ "label": "positive","id": "xyz" } ];
const sequence = ["abc","123"];
const sortData = (list,order) => {
const priority = {};
order.forEach((item,index) => (priority[item] = index));
return list.sort((itemA,itemB) => priority[itemA.id] - priority[itemB.id]);
};
console.log(sortData(data,sequence));
您正在寻找自定义排序功能。您的排序函数根据 id 值在原始输入数组中首次出现的位置做出决定。
const input = [
{label: "positive",id: "abc"},{label: "positive",id: "xyz"},{label: "negative",{label: "positive and negative",{label: "neg",id: "123"},];
const firstIdPos = input.reduce((a,c,i) => {
a[c.id] = a[c.id] ?? i;
return a;
},{});
console.log({firstIdPos});
console.log(input.sort((a,b) => firstIdPos[a.id] - firstIdPos[b.id]));
好吧,以下可能是我过火了,但这会给你一个 O(n) 时间复杂度。在这里,我们首先构建 LinkedList
,因为我们循环遍历您的 input
数组。 LinkedList
帮助我们O(1)插入节点,如果我们通过节点引用插入。我们只是根据 Map
中的 id 继续存储最新的节点引用。
最后,我们将该 LinkedList
中的条目转换为 array
。
我还没有对它进行彻底的测试,但我认为它应该可以工作。
当然,我正在使用空间来构建额外的 LinkedList
和一个 Map
(对于其他人共享的数组实现,您无论如何都必须这样做);
const input = [{
"label": "positive","id": "abc"
},{
"label": "positive","id": "xyz"
},{
"label": "negative",{
"label": "positive and negative",{
"label": "neg","id": "123"
},]
// Class Begin
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkedList {
constructor(data) {
this.head = new Node(data);
this.tail = this.head;
}
convertToArr() {
let temp = this.head;
const output = [];
while (temp !== null) {
output.push(temp.data);
temp = temp.next;
}
return output;
}
insertAfterNode(node,newNode) {
let previousNext = node.next;
node.next = newNode;
newNode.next = previousNext;
}
insertAtEnd(newNode) {
this.tail.next = newNode;
this.tail = newNode;
}
}
// Class End
// Function to transform input to output.
function transform(input) {
if (!input.length)
return [];
const ll = new LinkedList(input[0]);
const nodeCache = new Map()
nodeCache.set(input[0].id,ll.head);
for (let index = 1; index < input.length; index++) {
const data = input[index];
const newNode = new Node(data);
if (nodeCache.has(data.id))
ll.insertAfterNode(nodeCache.get(data.id),newNode);
else
ll.insertAtEnd(newNode);
nodeCache.set(data.id,newNode);
}
const output = ll.convertToArr();
return output;
}
const result = transform(input);
console.log(result);
这是该问题的通用解决方案。 sortByKey
获取您的 sortKey 数组并返回一个函数,该函数接受您的输入并按它们排序。 sortByKey
还可以选择采用一个函数,该函数从您的输入中提取您想要匹配的键。默认是采用 id
属性,但您可以使用任何您喜欢的内容覆盖它。你可以这样使用它:
const sortByKey = (sortKeys,keyGen = ({id}) => id) => (input) =>
sortKeys .flatMap (key => input.filter (x => keyGen(x) == key))
const sortKeys = ['abc','123']
const input = [{label: "positive",id: "xyz"}]
console .log (sortByKey (sortKeys) (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
但如果你选择,你也可以命名中间函数,像这样:
const mySort = sortByKey (['abc','123'])
// ... later
mySort (input)
在任何一种情况下,请注意此函数返回一个新数组,按您想要的方式排序。它不会修改原始数组;我们这里不是野蛮人。