Javascript 按字母顺序排序并将所有双精度移动到数组的末尾



我正在尝试按属性对对象数组进行排序。我跑:

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'},
]

预期成果:

艾米 克拉克 担 戴夫 乔 艾米 - 重复 克拉克 - 重复 乔 - 重复 克拉克 - 有第三个 乔 - 有第三个

如您所见,它按字母顺序对所有名称的第一次出现进行排序。然后按字母顺序对第二个匹配项进行排序,然后是第三个匹配项。直到解决所有重复项。

在评论中讨论之后,我了解到它不能单独在 array.sort 函数中完成。使用比较函数单独排序似乎非常适合单个或分组双打,但不适用于将双打放在数组的末尾。

您的比较器函数不正确。该函数必须:

  • 当第一个参数应在第二个参数之前排序时,返回一个负数;
  • 当第一个参数应在第二个参数之后排序时,返回一个正数;
  • 当两个项目具有等效的排序键时,返回零。

因为您的不一致,所以排序过程会变得混乱。 对于您的情况,最简单的使用方法是.localeCompare()函数,它返回的正是您需要的结果类型:

array.sort(function(a, b) { return a.name.localeCompare(b.name); });

从您的"预期输出"来看,您的订购标准不清楚。在任何情况下,排序比较器,无论它做什么,都必须是一致的:当两个项目以任一顺序传递给它时,函数应该报告相同的顺序。

编辑如果原始排序具有一些语义含义,并且"双精度"(我称它们为"重复项")应该在数组中进一步向下排序,您可以向捕获原始状态的每个对象添加另一个属性:

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);
});

如果"位置"值相同,则项目将按名称排序。原始数组中重复的项目将在非重复项之后排序(一式三份将在这些项目之后排序,依此类推)。

您可以通过对同一组数组使用带有哈希表的临时对象来使用带有 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; }

您可以先联接重复项并计算它们的出现次数:

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--;
}
}
}
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' } ]

派对有点晚了,但这绝对应该做到:

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)
)
);

最新更新