Javascript按字母顺序排序并将所有双精度数移动到数组的末尾
Posted
技术标签:
【中文标题】Javascript按字母顺序排序并将所有双精度数移动到数组的末尾【英文标题】:Javascript Alphabetically sort and move all doubles to the end of array 【发布时间】:2018-06-10 00:35:49 【问题描述】:我正在尝试按属性对对象数组进行排序。我跑:
array.sort(function(a, b)
var textA = a.name.toUpperCase();
var textB = b.name.toUpperCase();
return (textA < textB) ? -1 : (textA > textB) ? 1: 0
);
首先按字母顺序对数组对象进行排序,然后我运行一个带有自定义比较函数的array.sort,如下所示:
array.sort(function(a, b)
if(a.name === b.name)
return -1;
return 1;
);
它似乎适用于任何没有重复的对象,但是,一旦有双精度,它就会将它们全部推到数组的末尾,而不仅仅是附加值。
例子:
[
name: 'Amy',
name: 'Amy',
name: 'Clark',
name: 'Clark',
name: 'Dan',
name: 'Dave'
name: 'Joe',
name: 'Joe'
]
预期输出:
艾米 克拉克 丹 戴夫 乔 艾米 克拉克 乔实际结果:
丹 戴夫 艾米 艾米 克拉克 克拉克 乔 乔排序代码以尝试获得预期结果
array.sort(function(a,b)
if(a.name === b.name)return -1
return 1;
);
我感觉带有比较函数的 array.sort 可以处理这个问题,但是我一直在使用 0、-1、1 的返回值,并且似乎无法让它完全按照我的意愿工作。
更新
预期结果标准:
如果一个对象具有相同的名称,则副本应位于数组的末尾。例如,如果有两个“Amy”,则其中一个位于数组的开头,而副本则位于数组的末尾。这样所有第一次出现的名称都将位于数组的开头,并且所有双精度数、三元数等每次都将在数组末尾重新排序。这样它就可以潜在地安排多个项目。
例子:
[
name: 'Amy',
name: 'Amy',
name: 'Clark',
name: 'Clark',
name: 'Clark',
name: 'Dan',
name: 'Dave',
name: 'Joe',
name: 'Joe',
name: 'Joe',
]
预期结果:
艾米 克拉克 担 戴夫 乔 艾米 - 重复 克拉克 - 重复 乔 - 重复 克拉克 - 有三分之一 乔 - 有第三个
如您所见,它按字母顺序排列所有名称的第一次出现。然后按字母顺序排列第二个出现,然后是第三个。直到解决所有重复项。
在与 cmets 交谈之后,我了解到它不能单独在 array.sort 函数中完成。单独使用比较函数进行排序似乎非常适合单个或分组双打,但不适用于将双打放在数组的末尾。
【问题讨论】:
想要的输出是什么?预期的输出? 等等,为什么第二个“Amy”应该排在列表的最后?在所有情况下,“Amy”这个名字都应该在“Dan”和“Joe”之前,否则这个问题没有意义。 见***.com/a/6712058/6383857 这只是按字母顺序排序。这是按字母顺序排列的,数组末尾有重复项。 @L1ghtk3ira “在数组末尾重复”——这是什么意思?比如,确切地说,您期望的是什么? 【参考方案1】:您的比较器功能不正确。该函数必须:
当第一个参数应该排在第二个之前时,返回一个负数; 当第一个参数应该在第二个参数之后排序时,返回一个正数; 当两个项目具有相同的排序键时返回零。由于您的不一致,排序过程会变得混乱。对于您的情况,使用最简单的方法是 .localeCompare()
函数,它会返回您需要的结果:
array.sort(function(a, b) return a.name.localeCompare(b.name); );
从您的“预期输出”来看,您的订购标准尚不清楚。在任何情况下,排序比较器,无论它做什么,都必须是一致的:当两个项目以任一顺序传递给它时,该函数应该报告相同的顺序。
edit 如果原始排序具有某种语义含义,并且“双精度”(我称之为“重复”)应该在数组中进一步排序,您可以为每个排序添加另一个属性捕获原始状态的对象:
var keyMap = ;
array.forEach(function(item)
if (item.name in keyMap)
item.position = ++keyMap[item.name];
else
keyMap[item.name] = item.position = 1;
);
现在你可以排序了:
array.sort(function(a, b)
var c = a.position - b.position;
if (c) return c;
return a.name.localeCompare(b.name);
);
如果“位置”值相同,则项目将按名称排序。原始数组中重复的项目将排在不重复的项目之后(三次重复的项目将排在这些之后,等等)。
【讨论】:
@31piy 如果没有 OP 的更多解释,“预期输出”没有任何意义。 你需要知道什么? @L1ghtk3ira 您的订购标准是什么?为什么“Amy”会出现在“Dan”或“Joe”之后?如果您手动排序列表,您将如何决定一个项目应该在另一个项目之前还是之后? 有些对象名称相同,但其余数据不同。每个名字的第一个应该放在第一位。任何其他同名的都应该在数组的末尾。 这是一个奇怪的标准,但不是我的选择。【参考方案2】:您可以使用sorting with map,方法是将临时对象与同一组数组的哈希表一起使用。从中取出所用数组的长度作为分组进行排序。
排序发生在组和索引上。
结果映射到排序后的临时数组的索引。
Tge 第一部分生成一个数组,其中包含原始数组及其组的索引,该索引取自将一个值推入同一组。实际上,我们只需要推送组后的数组长度。如果多个项目在同一个组中,项目将稍后排序。
[ index: 0, // Amy group: 1 , index: 1, // Amy group: 2 , index: 2, // Dan group: 1 , index: 3, // Joe group: 1 , index: 4, // Joe group: 2 ]
上面给定的数组然后按组和索引排序,都是升序的。
在最后一部分,一个新的数组与排序后的数组的索引值进行映射。
var array = [ name: 'Amy' , name: 'Amy' , name: 'Dan' , name: 'Joe' , name: 'Joe' ],
groups = Object.create(null),
result = array
// this part is only necessary if the names should be in ascending order
// for keeping the given order, remove the part until next comment
.sort(function (a, b)
return a.name.localeCompare(b.name);
)
// remove until here, if necessary
.map(function (a, i)
return index: i, group: (groups[a.name] = groups[a.name] || []).push(0) ;
)
.sort(function (a, b)
return a.group - b.group || a.index - b.index;
)
.map(function (o)
return array[o.index];
);
console.log(result);
.as-console-wrapper max-height: 100% !important; top: 0;
未排序数据的示例。
var array = [ name: 'Joe', i: 0 , name: 'Dan', i: 1 , name: 'Amy', i: 2 , name: 'Joe', i: 3 , name: 'Amy', i: 4 ],
groups = Object.create(null),
result = array
.map(function (a, i)
return
index: i,
group: (groups[a.name] = groups[a.name] || []).push(0),
value: a.name
;
)
.sort(function (a, b)
return a.group - b.group || a.value.localeCompare(b.value);
)
.map(function (o)
return array[o.index];
);
console.log(result);
.as-console-wrapper max-height: 100% !important; top: 0;
【讨论】:
经过测试,这工作。你能提供额外的信息来解释它吗?谢谢 在添加更多描述排序方式的信息(如 Pointy 的方式)之后,我将标记为例外答案。它的工作原理看起来很干净,而且很小。 减去.group
数组似乎很奇怪。而且我看不出这是如何按名称排序的。
@Pointy,它采用给定名称的顺序并保持相同的顺序。
对,但它实际上并没有按名称排序。我对排序标准感到非常困惑,但我认为这是其中的一部分:所以如果“Amy”在原始数组中在“Dan”之后,结果应该是结果中的第一个“Amy”在“Dan”之前。【参考方案3】:
您可以先加入重复项并计算其出现次数:
const array = [
name: 'Amy',
name: 'Amy',
name: 'Dan',
name: 'Joe',
name: 'Joe'
];
const counted = [], byName = ;
for(var name of array)
if(byName[name])
byName[name].count++;
else
counted.push(byName[name] = name, count:1);
既然名称是唯一的,我们可以按字母顺序对它们进行排序:
counted.sort((a, b) => a.name.localeCompare(b.name));
最后,我们需要再次传播名称:
const result = [];
while(counted.length)
for(var i = 0; i < counted.length; i++)
const name = counted[i];
result.push(name.name);
name.count--;
if(!name.count)
counted.splice(i, 1);
i--;
【讨论】:
【参考方案4】:function compareSimple(a, b)
if (a > b)
return 1;
else if (a < b)
return -1;
return 0;
function compareAlphabetic(a, b)
return compareSimple(a.name.toUpperCase(), b.name.toUpperCase());
let input = [
name: 'Amy' ,
name: 'Amy' ,
name: 'Clark' ,
name: 'Clark' ,
name: 'Dan' ,
name: 'Clark' ,
name: 'Dave' ,
name: 'Joe' ,
name: 'Joe' ,
];
let output = input
.sort(compareAlphabetic)
.reduce(function(acc, curr)
let rank = 0
let prev = acc.length > 0 ? acc[acc.length-1] : null
if (prev && compareAlphabetic(prev.value, curr) === 0)
rank = prev.rank + 1
acc.push( value: curr, rank: rank );
return acc
, [])
// now we have an array like this
// [
// value: Amy, rank: 0,
// value: Amy, rank: 1,
// value: Clark, rank: 0,
// ...]
// Now let's sort it by rank AND name
.sort(function(a, b)
let result = compareSimple(a.rank, b.rank);
if (result !== 0)
return result;
return compareAlphabetic(a.value, b.value);
)
// we have to unpack it all back:
.map(function(obj)
return obj.value;
);
console.log(output);
// [ name: 'Amy' ,
// name: 'Clark' ,
// name: 'Dan' ,
// name: 'Dave' ,
// name: 'Joe' ,
// name: 'Amy' ,
// name: 'Clark' ,
// name: 'Joe' ,
// name: 'Clark' ]
【讨论】:
【参考方案5】:聚会有点晚了,但绝对应该这样做:
var arr = [
name: 'Amy',
name: 'Amy',
name: 'Clark',
name: 'Clark',
name: 'Clark',
name: 'Dan',
name: 'Dave',
name: 'Joe',
name: 'Joe',
name: 'Joe',
name: 'Joe',
name: 'Joe'
];
const o = arr.reduce(
(acc,item)=>
(acc[item.name]===undefined)
? acc[item.name]=1
: acc[item.name]+=1
return acc;
,
);
const highest = Object.keys(o).reduce(
(acc,item)=>
(o[item]>acc)
? o[item]
: acc
,1
);
const sort = (all,level=1,results=[]) =>
const dub = [""," - Duplicate"," - Had a third"," - Had a fourth"];
if(level>highest)
return results;
results = results.concat(
all.filter(
item=>
//if you don't want Clark and Joe to show up 3 times:
// level===1
// ? level<=o[item]
// : level===o[item]
level<=o[item]
)
.filter((item,index,all)=>all.indexOf(item)===index)
.sort()
.map(x=>
x+
(dub[level-1]===undefined
? " - more than four"
: dub[level-1]
)
)
);
return sort(all,level+1,results)
console.log(
sort(
arr.map(x=>x.name)
)
);
【讨论】:
以上是关于Javascript按字母顺序排序并将所有双精度数移动到数组的末尾的主要内容,如果未能解决你的问题,请参考以下文章