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

时间:2017-12-29 15:30:44

标签: 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
});

首先按字母顺序对数组对象进行排序,然后使用自定义比较函数运行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&#39;一个停留在数组的开头,副本就到了最后。因此,所有第一次出现的名称都将在数组的开头,并且每次在数组末尾都将重新排序所有双精度数,三重等。因此,它可能会安排alhpabetical多项。

示例:

[
  {name: 'Amy'},
  {name: 'Amy'},
  {name: 'Clark'},
  {name: 'Clark'},
  {name: 'Clark'},
  {name: 'Dan'},
  {name: 'Dave'},
  {name: 'Joe'},
  {name: 'Joe'},
  {name: 'Joe'},
]

预期结果:

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

正如您所看到的,它按字母顺序排列所有名称的首次出现。然后按字母顺序排序第二次,然后排第三次。直到所有重复项都得到解决。

在评论中谈到后,我的理解是它不能单独在array.sort函数中完成。单独使用比较函数排序似乎对单个或分组双精度有好处,但不适用于在数组末尾放置双精度数。

5 个答案:

答案 0 :(得分:5)

您的比较器功能不正确。该功能必须:

  • 当第一个参数在之前排序时,返回否定号码;
  • 当第一个参数在之后排序时,返回号;
  • 当两个项目具有相同的排序键时返回零。

因为你的不一致,排序过程会混淆。对于您的情况,最简单的方法是.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);
});

如果“position”值相同,则将按名称对项目进行排序。在原始数组中重复的项目将在非项目之后进行排序(并且三重之后的项目将按照这些项目排序等)。

答案 1 :(得分:4)

您可以通过使用具有相同组数组的哈希表的临时对象来使用sorting with map。从中获取已使用数组的长度作为排序组。

使用组和索引进行排序。

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

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

[
    {
        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
    }
]

然后上面给出的数组按组和索引进行排序,两者都是升序。

在最后一部分,将使用已排序数组的索引值映射新数组。

&#13;
&#13;
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);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

未分类数据的示例。

&#13;
&#13;
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);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 2 :(得分: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--;
    }
  }
}

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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