已根据@Kaddath的良好建议对问题进行了编辑,以突出显示以下事实:排序不必是字母顺序的,而是取决于数组中项目的位置。
我有一个数组数组,其中每个数组都基于给定的顺序,但是它们可以有所不同。
例如,基本顺序为X -> D -> H -> B
,这是我的数组数组:
const arrays = [
['X', 'D', 'H', 'B'],
['X', 'D', 'K', 'Z', 'H', 'B', 'A'],
['X', 'M', 'D', 'H', 'B'],
['X', 'H', 'T'],
['X', 'D', 'H', 'B']
]
我想将所有数组合并为一个数组,并删除重复项,但要保持顺序。在我的示例中,结果为['X', 'M', 'D', 'K', 'Z', 'H', 'T', 'B', 'A']
。
在该示例中,我们可以看到M
在第三个数组内的X
和D
之间,并且它被放置在X
和D
之间最终输出。
我知道可能会发生冲突,但这是以下规则:
到目前为止,我所做的是通过使用
将所有这些数组合并为一个数组const merged = [].concat.apply([], arrays);
(参见https://stackoverflow.com/a/10865042/3520621)。
然后通过使用https://stackoverflow.com/a/1584377/3520621中的以下代码片段来获取唯一值:
Array.prototype.unique = function() {
var a = this.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
};
const finalArray = merged.unique();
但是我的结果是这样的
[
"X",
"D",
"H",
"B",
"K",
"Z",
"A",
"M",
"T"
]
欢迎任何帮助!
谢谢。
答案 0 :(得分:3)
平整,删除重复项并进行排序可能更简单:
const arrays = [
['A', 'B', 'C', 'D'],
['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
['A', 'A-bis', 'B', 'C', 'D'],
['A', 'C', 'E'],
['A', 'B', 'C', 'D'],
];
console.log(
arrays
.flat()
.filter((u, i, all) => all.indexOf(u) === i)
.sort((a, b) => a.localeCompare(b)),
);
根据穆罕默德·乌斯曼(Mohammad Usman)现在已删除的帖子,事件更简单:
const arrays = [
['A', 'B', 'C', 'D'],
['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
['A', 'A-bis', 'B', 'C', 'D'],
['A', 'C', 'E'],
['A', 'B', 'C', 'D'],
];
console.log(
[...new Set([].concat(...arrays))].sort((a, b) =>
a.localeCompare(b),
),
);
答案 1 :(得分:1)
您可以将.concat()
与Set
一起使用,以得到唯一值的结果数组:
const data = [
['A', 'B', 'C', 'D'],
['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
['A', 'A-bis', 'B', 'C', 'D'],
['A', 'C', 'E'],
['A', 'B', 'C', 'D']
];
const result = [...new Set([].concat(...data))].sort((a, b) => a.localeCompare(b));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
答案 2 :(得分:1)
使用array#concat
创建单个数组,然后使用Set
从该数组中获取唯一值,然后对该数组进行排序。
const arrays = [ ['A', 'B', 'C', 'D'], ['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'], ['A', 'A-bis', 'B', 'C', 'D'], ['A', 'C', 'E'], ['A', 'B', 'C', 'D'] ],
result = [...new Set([].concat(...arrays))].sort();
console.log(result);
答案 3 :(得分:1)
好玩的问题要解决;我想我只能部分成功。
B -> A -> T
与T -> B -> A
的“未指定”示例仍然发布,因为我认为这可能会帮助您正确处理。这是我的方法:
我们正在创建一个对象,该对象针对嵌套数组中的每个唯一元素跟踪其成功或之前:
{
"X": { prev: Set({}), next: Set({ "D", "H", "B", "K", "Z", "A", "M", "T" })
"M": { prev: Set({ "X" }), next: Set({ "D", "H", "B" })
// etc.
}
我将其命名为“天真”,因为这些集合仅包含一层深度的信息。
即:它们仅报告同一数组中元素之间的关系。他们看不到
M
在K
之前,因为他们从来不在同一数组中。
在这里我忽略了所有可能涉及到的big-O问题。我递归合并索引:next
的{{1}}是M
的{{1}}的联接。递归直到找到没有next
的元素,即D, H, B
或next
。
T
此函数创建一个排序方法,该方法使用生成的索引查找任意两个元素之间的排序顺序。
A
我认为,通过使用链表/树状结构并遍历直到其元素插入正确索引处的元素,该问题的优雅解决方案可能能够跳过const indexSorter = idx => (a, b) =>
idx[a].next.has(b) || idx[b].prev.has(a) ? -1 :
idx[a].prev.has(b) || idx[b].next.has(a) ? 1 :
0 ;
的所有合并找到(function() {
const naiveSortIndex = xss => xss
.map(xs =>
// [ prev, cur, next ]
xs.map((x, i, xs) => [
xs.slice(0, i), x, xs.slice(i + 1)
])
)
// flatten
.reduce((xs, ys) => xs.concat(ys), [])
// add to index
.reduce(
(idx, [prev, cur, next]) => {
if (!idx[cur])
idx[cur] = {
prev: new Set(),
next: new Set()
};
prev.forEach(p => {
idx[cur].prev.add(p);
});
next.forEach(n => {
idx[cur].next.add(n);
});
return idx;
}, {}
);
const expensiveSortIndex = xss => {
const naive = naiveSortIndex(xss);
return Object
.keys(naive)
.reduce(
(idx, k) => Object.assign(idx, {
[k]: {
prev: mergeDir("prev", naive, k),
next: mergeDir("next", naive, k)
}
}), {}
)
}
const mergeDir = (dir, idx, k, s = new Set()) =>
idx[k][dir].size === 0
? s
: Array.from(idx[k][dir])
.reduce(
(s, k2) => mergeDir(dir, idx, k2, s),
new Set([...s, ...idx[k][dir]])
);
// Generate a recursive sort method based on an index of { key: { prev, next } }
const indexSorter = idx => (a, b) =>
idx[a].next.has(b) || idx[b].prev.has(a) ? -1 :
idx[a].prev.has(b) || idx[b].next.has(a) ? 1 :
0;
const uniques = xs => Array.from(new Set(xs));
// App:
const arrays = [
['X', 'D', 'H', 'B'],
['X', 'D', 'K', 'Z', 'H', 'B', 'A'],
['X', 'M', 'D', 'H', 'B'],
['X', 'H', 'T'],
['X', 'D', 'H', 'B']
];
const sortIndex = expensiveSortIndex(arrays);
const sorter = indexSorter(sortIndex);
console.log(JSON.stringify(
uniques(arrays.flat()).sort(sorter)
))
}())
/ Set
。
答案 4 :(得分:1)
const arrays = [
['X', 'D', 'H', 'B'],
['X', 'D', 'K', 'Z', 'H', 'B', 'A'],
['X', 'M', 'D', 'H', 'B'],
['X', 'H', 'T'],
['X', 'D', 'H', 'B']
];
const result = [];
arrays.forEach(array => {
array.forEach((item, idx) => {
// check if the item has already been added, if not, try to add
if(!~result.indexOf(item)) {
// if item is not first item, find position of his left sibling in result array
if(idx) {
const result_idx = result.indexOf(array[idx - 1]);
// add item after left sibling position
result.splice(result_idx + 1, 0, item);
return;
}
result.push(item);
}
});
});
console.log('expected result', ['X', 'M', 'D', 'K', 'Z', 'H', 'T', 'B', 'A'].join(','));
console.log(' current result',result.join(','));
答案 5 :(得分:0)
为此使用BST。将所有元素添加到bst中,然后按顺序遍历。
function BST(){
this.key = null;
this.value = null;
this.left = null;
this.right = null;
this.add = function(key}{
const val = key;
key = someOrderFunction(key.replace(/\s/,''));
if(this.key == null) {
this.key = key;
this.val = val;
} else if(key < this.key) {
if(this.left){
this.left.add(val);
} else {
this.left = new BST();
this.left.key = key;
this.left.val = val;
}
} else if(key > this.key) {
if(this.right){
this.right.add(val);
} else {
this.right= new BST();
this.right.key = key;
this.right.val = val;
}
}
this.inOrder = function(){
const leftNodeOrder = this.left ? this.left.inOrder() : [],
rightNodeOrder = this.right? this.right.inOrder() : [];
return leftNodeOrder.concat(this.val).concat(this.rightNodeOrder);
}
}
// MergeArrays uses a BST to insert all elements of all arrays
// and then fetches them sorted in order
function mergeArrays(arrays) {
const bst = new BST();
arrays.forEach(array => array.forEach( e => bst.add(e)));
return bst.inOrder();
}
答案 6 :(得分:0)
我只是将数组展平,将它们作为键映射到对象(从而删除双精度),然后对最终结果进行排序
const arrays = [
['A', 'B', 'C', 'D'],
['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
['A', 'A-bis', 'B', 'C', 'D'],
['A', 'C', 'E'],
['A', 'B', 'C', 'D']
];
const final = Object.keys( arrays.flat().reduce( (aggregate, entry) => {
aggregate[entry] = '';
return aggregate;
}, {} ) ).sort( (x1, x2) => x1.localeCompare(x2) );
console.log( final );
答案 7 :(得分:0)
[].concat.apply([], arrays)
[...new Set(merged)]
.sort()
进行排序
const arrays = [
['A', 'B', 'C', 'D'],
['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
['A', 'A-bis', 'B', 'C', 'D'],
['A', 'C', 'E'],
['A', 'B', 'C', 'D']
];
let merged = [].concat.apply([], arrays); // merge array
let sort = [...new Set(merged)].sort(); // find uniq then sort
console.log(sort);
答案 8 :(得分:0)
对于您的代码,合并后,您需要删除重复项。这样您将获得唯一的数组。
使用array.sort对数组进行排序。
我希望这能解决问题。
const arrays = [
['A', 'B', 'C', 'D'],
['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
['A', 'A-bis', 'B', 'C', 'D'],
['A', 'C', 'E'],
['A', 'B', 'C', 'D']
]
const merged = [].concat.apply([], arrays);
const unique = Array.from(new Set(merged))
const sorted = unique.sort()
console.log("sorted Array", sorted)
// Single Line
const result = [...new Set([].concat(...arrays))].sort();
console.log("sorted Array single line", result)
答案 9 :(得分:0)
每个数组实际上都是一组规则,它们告诉元素之间的相对顺序是什么。最终列表应在遵守所有规则定义的相对顺序的同时返回所有元素。
一些解决方案已经解决了最初的请求,有些甚至没有解决该问题(所有建议都使用某种方式遗漏了问题的要点)。但是,没有人提出通用解决方案。
如果我们查看OP中提出的问题,这就是规则如何定义元素之间的相对位置:
M K -> Z T
^ \ ^ \ ^
/ v/ v/
X -> D ------> H -> B -> A
因此,很容易看到我们的数组以X开头。下一个元素可以是D和M。但是,D要求M已经在数组中。这就是为什么我们将M设为下一个元素,然后设为D的原因。接下来,D指向K和H。但是,由于H之前还没有收集到其他的前任元素,而K却没有(实际上它有D,但已经在列表中了),我们将K和Z放在一起,然后再放H。
H指向T和B。实际上,我们首先输入哪个都无所谓。因此,最后三个元素可以按以下三个顺序中的任何一个排列:
让我们也考虑一些更复杂的情况。规则如下:
['10', '11', '12', '1', '2'],
['11', '12', '13', '2'],
['9', '13'],
['9', '10'],
如果使用这些规则绘制图形,则会得到以下结果:
--------------> 13 ----
/ ^ \
/ / v
9 -> 10 -> 11 -> 12 > 1 -> 2
此案的具体内容是什么?两件事:
我的想法是从每个元素创建一个节点。然后使用该节点来跟踪所有直接后继者和直接前任者。之后,我们将找到所有没有前任的元素,然后从那里开始“收集”结果。如果我们到达具有多个前任节点的节点,但是其中一些未收集,我们将在此处停止递归。有可能某些继任者已经通过其他途径获得了。我们将跳过该继任者。
function mergeAndMaintainRelativeOrder(arrays/*: string[][]*/)/*: string[]*/ {
/*
interface NodeElement {
value: string;
predecessor: Set<NodeElement>;
successor: Set<NodeElement>;
collected: boolean;
}
*/
const elements/*: { [key: string]: NodeElement }*/ = {};
// For every element in all rules create NodeElement that will
// be used to keep track of immediate predecessors and successors
arrays.flat().forEach(
(value) =>
(elements[value] = {
value,
predecessor: new Set/*<NodeElement>*/(),
successor: new Set/*<NodeElement>*/(),
// Used when we form final array of results to indicate
// that this node has already be collected in final array
collected: false,
}),
);
arrays.forEach((list) => {
for (let i = 0; i < list.length - 1; i += 1) {
const node = elements[list[i]];
const nextNode = elements[list[i + 1]];
node.successor.add(nextNode);
nextNode.predecessor.add(node);
}
});
function addElementsInArray(head/*: NodeElement*/, array/*: string[]*/) {
let areAllPredecessorsCollected = true;
head.predecessor.forEach((element) => {
if (!element.collected) {
areAllPredecessorsCollected = false;
}
});
if (!areAllPredecessorsCollected) {
return;
}
array.push(head.value);
head.collected = true;
head.successor.forEach((element) => {
if (!element.collected) {
addElementsInArray(element, array);
}
});
}
const results/*: string[]*/ = [];
Object.values(elements)
.filter((element) => element.predecessor.size === 0)
.forEach((head) => {
addElementsInArray(head, results);
});
return results;
}
console.log(mergeAndMaintainRelativeOrder([
['X', 'D', 'H', 'B'],
['X', 'D', 'K', 'Z', 'H', 'B', 'A'],
['X', 'M', 'D', 'H', 'B'],
['X', 'H', 'T'],
['X', 'D', 'H', 'B'],
]));
console.log(mergeAndMaintainRelativeOrder([
['10', '11', '12', '1', '2'],
['11', '12', '13', '2'],
['9', '13'],
['9', '10'],
]));
如果我们说n
是规则数,而m
是每个规则中的元素数,则此算法的复杂度为 O(n * m)。这考虑到了Set implementation for the JS is near O(1)。