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

Javascript按字母顺序排序并将所有的双精度字符移动到数组的末尾,javascript,arrays,sorting,object,Javascript,Arrays,Sorting,Object,我正在尝试按属性对对象数组进行排序。我运行: array.sort(function(a, b){ var textA = a.name.toUpperCase(); var textB = b.name.toUpperCase(); return (textA < textB) ? -1 : (textA > textB) ? 1: 0 }); 它似乎适用于任何没有重复项的对象,但是,一旦有重复项,它就会将它们全部推到数组的末尾,而不仅仅是多余项 例如: [ {n

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

array.sort(function(a, b){
  var textA = a.name.toUpperCase();
  var textB = b.name.toUpperCase();
  return (textA < textB) ? -1 : (textA > textB) ? 1: 0
});
它似乎适用于任何没有重复项的对象,但是,一旦有重复项,它就会将它们全部推到数组的末尾,而不仅仅是多余项

例如:

[
  {name: 'Amy'},
  {name: 'Amy'},
  {name: 'Clark'},
  {name: 'Clark'},
  {name: 'Dan'},
  {name: 'Dave'}
  {name: 'Joe'},
  {name: 'Joe'}
]
[
  {name: 'Amy'},
  {name: 'Amy'},
  {name: 'Clark'},
  {name: 'Clark'},
  {name: 'Clark'},
  {name: 'Dan'},
  {name: 'Dave'},
  {name: 'Joe'},
  {name: 'Joe'},
  {name: 'Joe'},
]
预期产出:

  • 艾米
  • 克拉克
  • 戴夫
  • 艾米
  • 克拉克
实际结果:

  • 戴夫
  • 艾米
  • 艾米
  • 克拉克
  • 克拉克
对代码进行排序以尝试获得预期结果

array.sort(function(a,b){
  if(a.name === b.name){return -1}

  return 1;
});
我感觉array.sort和compare函数可以处理这个问题,但是我一直在使用返回值0,-1,1,似乎无法让它像我希望的那样完全工作

更新

预期结果标准:

如果一个对象具有相同的名称,则副本应位于数组的末尾。例如,如果有两个“Amy”,则其中一个位于数组的开头,而副本则位于数组的结尾。因此,所有第一次出现的名称都将在数组的开头,并且所有的双精度、三精度等将在数组的末尾重新排序。这样它就可以潜在地安排多个项目

例如:

[
  {name: 'Amy'},
  {name: 'Amy'},
  {name: 'Clark'},
  {name: 'Clark'},
  {name: 'Dan'},
  {name: 'Dave'}
  {name: 'Joe'},
  {name: 'Joe'}
]
[
  {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); });
从您的“预期输出”来看,您的订购标准不明确。在任何情况下,排序比较器,无论它做什么,都必须是一致的:当两个项以任意顺序传递给它时,函数应该报告相同的顺序

编辑如果原始顺序具有某种语义含义,并且“double”(我称之为“duplicates”)应该在数组中进一步排序,则可以向捕获原始状态的每个对象添加另一个属性:

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);
});
如果“位置”值相同,则项目将按名称排序。原始数组中重复的项将在未重复的项之后排序(重复的项将在这些项之后排序,以此类推)。

您可以使用临时对象和同一组数组的哈希表。从中获取用作排序组的数组的长度

排序与组和索引一起进行

结果与已排序临时数组的索引映射

Tge第一部分生成一个数组,其中包含原始数组及其组的索引,该索引是从将值推入同一组中获取的。事实上,我们需要在组的推进后得到数组长度的oly。如果同一组中有更多项目,则稍后将对这些项目进行排序

然后按组和索引对上述给定数组进行排序,两者都是升序

在最后一部分,一个新数组被映射为排序数组的索引值

var数组=[{name:'Amy'},{name:'Amy'},{name:'Dan'},{name:'Joe'},{name:'Joe'}],
groups=Object.create(空),
结果=数组
//仅当名称应按升序排列时,才需要此部分
//为了保持给定的顺序,请在下一次注释之前删除该零件
.排序(功能(a、b){
返回a.name.localeCompare(b.name);
})
//如有必要,将其移到此处
.map(函数(a,i){
返回{index:i,group:(groups[a.name]=groups[a.name]| |[]).push(0)};
})
.排序(功能(a、b){
返回a.group-b.group | a.index-b.index;
})
.map(功能(o){
返回数组[o.index];
});
控制台日志(结果)

.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--;
    }
  }
}
const result=[];
while(计算长度){
对于(变量i=0;i
函数比较示例(a、b){
如果(a>b){
返回1;
}否则如果(a0?附件[附件长度-1]:空
if(上一个值和比较值)(上一个值,当前)==0){
等级=上一等级+1
}
acc.push({value:curr,rank:rank});
返回acc
},
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' } ]