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按字母顺序排序并将所有双精度数移动到数组的末尾的主要内容,如果未能解决你的问题,请参考以下文章

按首字母 Javascript 按字母顺序对对象进行排序和分组

在Javascript中按字母顺序对字符串进行排序

在Javascript中按字母顺序对字符串进行排序

按字母顺序排序时忽略变音符号

按字母顺序排序列表

按字母顺序对HashMap程序的输出进行排序[重复]