【问题标题】:Sort an array except one element in JavaScript对 JavaScript 中除一个元素外的数组进行排序
【发布时间】:2023-03-14 12:55:01
【问题描述】:

我有一个数组,我正在对其进行排序,但我需要对除数组中的一个元素之外的所有内容进行排序。

我的数组是:

var Comparison = [
    {key: "None", value: "None"},
    {key:"Geographical Area", value:"Geographical_Area"},
    {key:"Forests", value:"Forests"},
    {key:"Barren Unculturable Land", value:"Barren_Unculturable_Land"},
    {key:"Land put to Non agricultural use", value:"Land_put_to_Non_agricultural_use"},
    {key:"Land Area", value:"Land_Area"},
    {key:"Water Area", value:"Water_Area"},
    {key:"Culturable Waste", value:"Culturable_Waste"},
    {key:"Permanent Pastures", value:"Permanent_Pastures"},
    {key:"Land under Tree Crops", value:"Land_under_Tree_Crops"},
    {key:"Fallow Land excl Current Fallow", value:"Fallow_Land_excl_Current_Fallow"},
    {key:"Current Fallow", value:"Current_Fallow"},
    {key:"Total Unculturable Land", value:"Total_Unculturable_Land"},
    {key:"Net Sown Area", value:"Net_Sown_Area"},
    {key:"Gross Sown Area", value:"Gross_Sown_Area"},
    {key:"Cropping Intensity", value:"Cropping_Intensity"} ];

我正在使用以下代码对该数组进行排序:

var Comparison_sort = this.Comparison.sort(function (a, b) {
  if (a.key < b.key)
      return -1;
  if (a.key > b.key)
      return 1;
  return 0;
});

这是对我的数组进行完美排序,但我希望我的元素之一位于顶部,这意味着我的元素 None 应该位于顶部并对所有其他元素进行排序。

例如,我得到这个结果:

   {key: "Barren Unculturable Land", value: "Barren_Unculturable_Land"}
   {key: "Cropping Intensity", value: "Cropping_Intensity"}
   {key: "Culturable Waste", value: "Culturable_Waste"}
    ....
   {key: "None", value: "None"}

但我想要这样的结果:

   {key: "None", value: "None"}
   {key: "Barren Unculturable Land", value: "Barren_Unculturable_Land"}
   {key: "Cropping Intensity", value: "Cropping_Intensity"}
   {key: "Culturable Waste", value: "Culturable_Waste"}
    ....

我看到了一个答案,Sort array in TypeScript,但我无法使用这个答案来解决我的问题。

【问题讨论】:

  • 只检查key是否为none,返回包含该key的对象的排序值为-1?
  • 给它一个特殊的价值。将其从您的数组中过滤出来,对其进行排序,然后将其放回您想要的位置。或者在比较函数中检查它,确保它总是“小于其他”(正如 KarelG 建议的那样)
  • @DaveNewton 您的意思是数组中是否有一个None,而不是第一个位置,或者您的意思是数组中是否有多个None
  • @DaveNewton 如果集合中没有"none",则永远不会执行分支if (a.key === "none") { return -1} ...如果放在第一位。
  • @DaveNewton 我现在明白你的意思了。有了 spi 的回答,它工作正常。

标签: javascript arrays sorting


【解决方案1】:
var Comparison_sort = this.Comparison.sort(function (a, b) {
  if(a.key == b.key) return 0;
  if (a.key == 'None') return -1;
  if (b.key == 'None') return 1;

  if (a.key < b.key)
      return -1;
  if (a.key > b.key)
      return 1;
  return 0;
});

告诉“进行常规排序,除非键为 none,这意味着它必须先进行。”

【讨论】:

    【解决方案2】:

    并不花哨,但一种非常简单的方法是删除特殊元素,对数组进行排序,然后将特殊元素插入到您想要的任何索引中。

    var Comparison = [{ key: "None", value: "None" }, { key: "Geographical Area",value: "Geographical_Area" }, { key: "Forests", value: "Forests" }, { key: "Barren Unculturable Land", value: "Barren_Unculturable_Land" }, { key: "Land put to Non agricultural use", value: "Land_put_to_Non_agricultural_use" }, { key: "Land Area", value: "Land_Area" }, { key: "Water Area", value: "Water_Area" }, { key: "Culturable Waste", value: "Culturable_Waste" }, { key: "Permanent Pastures", value: "Permanent_Pastures" }, { key: "Land under Tree Crops", value: "Land_under_Tree_Crops" }, { key: "Fallow Land excl Current Fallow", value: "Fallow_Land_excl_Current_Fallow" }, { key: "Current Fallow", value: "Current_Fallow" }, { key: "Total Unculturable Land", value: "Total_Unculturable_Land" }, { key: "Net Sown Area", value: "Net_Sown_Area" }, { key: "Gross Sown Area", value: "Gross_Sown_Area" }, { key: "Cropping Intensity", value: "Cropping_Intensity" },];
    
    const idx = Comparison.findIndex(a => a.key === 'None');
    const none = Comparison.splice(idx, 1);
    Comparison.sort((a, b) => a.key.localeCompare(b.key));
    Comparison.splice(0,0, none[0]);
    
    console.log(Comparison);

    为避免没有特殊或多个特殊元素的问题:

    var Comparison = [{ key: "None", value: "None" }, { key: "Geographical Area",value: "Geographical_Area" }, { key: "Forests", value: "Forests" }, { key: "Barren Unculturable Land", value: "Barren_Unculturable_Land" }, { key: "Land put to Non agricultural use", value: "Land_put_to_Non_agricultural_use" }, { key: "Land Area", value: "Land_Area" }, { key: "Water Area", value: "Water_Area" }, { key: "Culturable Waste", value: "Culturable_Waste" }, { key: "Permanent Pastures", value: "Permanent_Pastures" }, { key: "Land under Tree Crops", value: "Land_under_Tree_Crops" }, { key: "Fallow Land excl Current Fallow", value: "Fallow_Land_excl_Current_Fallow" }, { key: "Current Fallow", value: "Current_Fallow" }, { key: "Total Unculturable Land", value: "Total_Unculturable_Land" }, { key: "Net Sown Area", value: "Net_Sown_Area" }, { key: "Gross Sown Area", value: "Gross_Sown_Area" }, { key: "Cropping Intensity", value: "Cropping_Intensity" },];
    
    const obj = Comparison.reduce((acc, a) => {
      if (a.key === 'None') {
        acc.f.push(a);
      } else {
        const idx = acc.s.findIndex(b => b.key.localeCompare(a.key) > 0);
        acc.s.splice(idx === -1 ? acc.s.length : idx, 0, a);
      }
      return acc;
    }, { f: [], s: [] });
    
    const res = obj.f.concat(obj.s);
    
    console.log(res);

    【讨论】:

    • 谢谢@JohanWentholt
    • 要记住的一点是,它不适用于缺少元素 'None' 或多个元素 'None'。在没有元素的情况下,'None' findIndex 将返回 -1.splice(-1, 1) 将切掉最后一个元素并将其放回前面。在多个元素的情况下,只有第一个元素 'None' 被放回第一个位置。但对于一个元素,这是一个完美的解决方案。
    • 是的,这完全正确,但我只是认为这里不是这样
    • 我同意你的观点,这是一个很好的解决方案。以上只是对偶然发现问题并且没有完全了解问题背景的读者的警告。
    【解决方案3】:

    或者,您可以过滤掉非元素并对其他元素进行排序。然后在最后将它们相互连接起来。

    const comparison = [{key: "None", value: "None"}, {key: "Geographical Area", value: "Geographical_Area"}, {key: "Forests", value: "Forests"}, {key: "Barren Unculturable Land", value: "Barren_Unculturable_Land"}, {key: "Land put to Non agricultural use", value: "Land_put_to_Non_agricultural_use"}, {key: "Land Area", value: "Land_Area"}, {key: "Water Area", value: "Water_Area"}, {key: "Culturable Waste", value: "Culturable_Waste"}, {key: "Permanent Pastures", value: "Permanent_Pastures"}, {key: "Land under Tree Crops", value: "Land_under_Tree_Crops"}, {key: "Fallow Land excl Current Fallow", value: "Fallow_Land_excl_Current_Fallow"}, {key: "Current Fallow", value: "Current_Fallow"}, {key: "Total Unculturable Land", value: "Total_Unculturable_Land"}, {key: "Net Sown Area", value: "Net_Sown_Area"}, {key: "Gross Sown Area", value: "Gross_Sown_Area"}, {key: "Cropping Intensity", value: "Cropping_Intensity"}];
    
    const result = comparison
                   .filter(e => e.key === 'None')
                   .concat(
                     comparison.filter(e => e.key !== 'None')
                               .sort((a, b) => a.key.localeCompare(b.key))
                   );
                   
    console.log(result);

    解释:

    const comparison = [{key: "None", value: "None"}, {key: "Geographical Area", value: "Geographical_Area"}, {key: "Forests", value: "Forests"}, {key: "Barren Unculturable Land", value: "Barren_Unculturable_Land"}, {key: "Land put to Non agricultural use", value: "Land_put_to_Non_agricultural_use"}, {key: "Land Area", value: "Land_Area"}, {key: "Water Area", value: "Water_Area"}, {key: "Culturable Waste", value: "Culturable_Waste"}, {key: "Permanent Pastures", value: "Permanent_Pastures"}, {key: "Land under Tree Crops", value: "Land_under_Tree_Crops"}, {key: "Fallow Land excl Current Fallow", value: "Fallow_Land_excl_Current_Fallow"}, {key: "Current Fallow", value: "Current_Fallow"}, {key: "Total Unculturable Land", value: "Total_Unculturable_Land"}, {key: "Net Sown Area", value: "Net_Sown_Area"}, {key: "Gross Sown Area", value: "Gross_Sown_Area"}, {key: "Cropping Intensity", value: "Cropping_Intensity"}];
    
    // fetch all elements with the key 'None'
    const nones = comparison.filter(e => e.key === 'None');
    // fetch all elements with the key not 'None'
    const others = comparison.filter(e => e.key !== 'None')
    // sort the elements in the array by key
                             .sort((a, b) => a.key.localeCompare(b.key));
    // concatenate the 2 arrays together
    const result = nones.concat(others);
    
    console.log(result);

    感谢Pac0s answer。写完我的解决方案后,我看到我基本上做了他解释的工作版本。我来不及将我的示例添加到他的答案中,因为这是目前两者中最受支持的。


    对于较大的数组,在具有相反谓词(检查回调)的同一个数组上使用两次 filter() 可能会有点低效。您可以选择引入像 partition() 这样的辅助函数来帮助减少必须完成的迭代量。

    function partition(forEachable, callback) {
      const partitions = { "true": [], "false": [] };
      forEachable.forEach((...args) => partitions[!!callback(...args)].push(args[0]));
      return [partitions[true], partitions[false]];
    }
    
    let comparison = [{key: "None", value: "None"}, {key: "Geographical Area", value: "Geographical_Area"}, {key: "Forests", value: "Forests"}, {key: "Barren Unculturable Land", value: "Barren_Unculturable_Land"}, {key: "Land put to Non agricultural use", value: "Land_put_to_Non_agricultural_use"}, {key: "Land Area", value: "Land_Area"}, {key: "Water Area", value: "Water_Area"}, {key: "Culturable Waste", value: "Culturable_Waste"}, {key: "Permanent Pastures", value: "Permanent_Pastures"}, {key: "Land under Tree Crops", value: "Land_under_Tree_Crops"}, {key: "Fallow Land excl Current Fallow", value: "Fallow_Land_excl_Current_Fallow"}, {key: "Current Fallow", value: "Current_Fallow"}, {key: "Total Unculturable Land", value: "Total_Unculturable_Land"}, {key: "Net Sown Area", value: "Net_Sown_Area"}, {key: "Gross Sown Area", value: "Gross_Sown_Area"}, {key: "Cropping Intensity", value: "Cropping_Intensity"}];
    
    const [nones, others] = partition(comparison, e => e.key === "None");
    others.sort((a, b) => a.key.localeCompare(b.key));
    const result = nones.concat(others);
    console.log(result);

    【讨论】:

      【解决方案4】:

      可能有更好的方法,但这应该可行:

      1. 从数组中过滤掉特殊值。

      2. 对没有特殊值的数组进行排序。

      3. 将特殊值重新插入到数组中。

      对于一个很好的工作示例,请参阅@Johan Wentholt's answer

      【讨论】:

      • 不必要的复杂。需要创建 2 个数组(缩减/扩展)
      • @spi 我当然也更喜欢你的答案,正在进行的评论讨论让我有疑问,我想提供另一个正确的答案,而不会陷入讨论的案例。虽然我知道这不是最佳答案,而且对于一些不值得投赞成票的人,我也不确定我的答案是否值得投反对票。
      • 如果你编辑我会删除不赞成票,但确实不值得赞成:)
      • @Pac0 我刚刚看到我的答案和你的一样,我应该在你的答案下添加我正在运行的 JavaScript 示例吗? (既然你先回答了。)你的解释也更清楚了。
      • @JohanWentholt 不确定我们所说的复杂性是否会在运行时产生任何开销(与创建 2 个无用数组相反)。所有这些 if 都是 OP 的原始代码 + 使用相同编码风格的修复。但我同意 OP 应该使用类似 localCompare 的函数来避免重新实现轮子。
      【解决方案5】:

      您可以使用reduce 来实现所需的输出:

      var Comparison = [{key:"Geographical Area", value:"Geographical_Area"},   {key:"Forests", value:"Forests"},   {key:"Barren Unculturable Land", value:"Barren_Unculturable_Land"}, {key: "None", value: "None"},  {key:"Land put to Non agricultural use", value:"Land_put_to_Non_agricultural_use"}, {key:"Land Area", value:"Land_Area"},   {key:"Water Area", value:"Water_Area"}, {key:"Culturable Waste", value:"Culturable_Waste"}, {key:"Permanent Pastures", value:"Permanent_Pastures"}, {key:"Land under Tree Crops", value:"Land_under_Tree_Crops"},   {key:"Fallow Land excl Current Fallow", value:"Fallow_Land_excl_Current_Fallow"},   {key:"Current Fallow", value:"Current_Fallow"}, {key:"Total Unculturable Land", value:"Total_Unculturable_Land"},   {key:"Net Sown Area", value:"Net_Sown_Area"},   {key:"Gross Sown Area", value:"Gross_Sown_Area"},   {key:"Cropping Intensity", value:"Cropping_Intensity"},]
      
      var Comparison_sort = Comparison
                            .sort((a, b) => a.key.localeCompare(b.key))
                            .reduce((acc, e) => {
                              e.key === 'None' ? acc.unshift(e) : acc.push(e);
                              return acc;
                            }, []);
      
      console.log(Comparison_sort);

      使用reduce version-2 排序:

      let comparison = [{key: "None", value: "None"}, {key: "Geographical Area", value: "Geographical_Area"}, {key: "Forests", value: "Forests"}, {key: "Barren Unculturable Land", value: "Barren_Unculturable_Land"}, {key: "Land put to Non agricultural use", value: "Land_put_to_Non_agricultural_use"}, {key: "Land Area", value: "Land_Area"}, {key: "Water Area", value: "Water_Area"}, {key: "Culturable Waste", value: "Culturable_Waste"}, {key: "Permanent Pastures", value: "Permanent_Pastures"}, {key: "Land under Tree Crops", value: "Land_under_Tree_Crops"}, {key: "Fallow Land excl Current Fallow", value: "Fallow_Land_excl_Current_Fallow"}, {key: "Current Fallow", value: "Current_Fallow"}, {key: "Total Unculturable Land", value: "Total_Unculturable_Land"}, {key: "Net Sown Area", value: "Net_Sown_Area"}, {key: "Gross Sown Area", value: "Gross_Sown_Area"}, {key: "Cropping Intensity", value: "Cropping_Intensity"}];
      
      var {Comparison_sort} = comparison.reduce((acc, obj, idx, arr) => {
                                        obj.key === 'None' ? acc['first'].push(obj) : acc['last'].push(obj)
                                        if (idx === arr.length - 1) (acc['last'].sort((a, b) => a.key.localeCompare(b.key)), acc['Comparison_sort'] = [...acc['first'], ...acc['last']])
                                        return acc
                                      }, {first: [], last: [], Comparison_sort: []})
      
      console.log(Comparison_sort);

      【讨论】:

      • 过度工程。
      • 是的,但是……这似乎很奢侈。
      • 您使用reduce 的解决方案非常好。更改后它与spis answer基本相同。介意我清理一下并将逻辑改回使用reduce吗? (因为这就是这个答案与其他答案的不同之处。)
      【解决方案6】:

      一个简单的单行:如果Array.prototype.sort比较函数中的任何一个键是'None',那么总是把它放在最上面,否则用String.prototype.localeCompare()做基本的比较:

      var comparison = [{key: "None", value: "None"}, {key: "Geographical Area", value: "Geographical_Area"}, {key: "Forests", value: "Forests"}, {key: "Barren Unculturable Land", value: "Barren_Unculturable_Land"}, {key: "Land put to Non agricultural use", value: "Land_put_to_Non_agricultural_use"}, {key: "Land Area", value: "Land_Area"}, {key: "Water Area", value: "Water_Area"}, {key: "Culturable Waste", value: "Culturable_Waste"}, {key: "Permanent Pastures", value: "Permanent_Pastures"}, {key: "Land under Tree Crops", value: "Land_under_Tree_Crops"}, {key: "Fallow Land excl Current Fallow", value: "Fallow_Land_excl_Current_Fallow"}, {key: "Current Fallow", value: "Current_Fallow"}, {key: "Total Unculturable Land", value: "Total_Unculturable_Land"}, {key: "Net Sown Area", value: "Net_Sown_Area"}, {key: "Gross Sown Area", value: "Gross_Sown_Area"}, {key: "Cropping Intensity", value: "Cropping_Intensity"}];
      
      var sorted = comparison.sort((a,b) => a.key === 'None' ? -1 : b.key === 'None' ? 1 : a.key.localeCompare(b.key));
      
      console.log(sorted);

      【讨论】:

        【解决方案7】:

        &lt;Array&gt;.sort 函数将回调作为参数。此回调将传递两个值。回调的工作是确定哪个更大。它通过返回一个数值来做到这一点。

        假设传递给回调的参数称为ab。我已将您的回调应为每种情况返回的值加粗

        • a &lt; b 小于 0
        • a &gt; b 大于 0
        • a = b 等于 0

        这很容易记住,因为对于数值,您可以使用a - b 来获得所需的返回值。

        现在,尽管传递给.sort 的大多数回调都非常小, 可以传递非常复杂的函数以满足您的需要。在这种情况下,

        • 如果a.key 为无,a &lt; b
        • 如果b.key 为无,b &lt; a
        • 否则,请使用我们当前的排序机制。

        我们可以利用return 语句一旦被调用就退出。所以,让我们逐条实现这个函数。

        为了让我们的代码超级好,让我们在两个值相等时返回“0”(即使这两个值的键为“None”)

        Comparison.sort(function(a, b) {
          // Our extra code
          if(a.key === b.key) return 0; // Zero (a = b)
          if(a.key === "None") return -1; // Negative (a < b)
          if(b.key === "None") return 1; // Positive (b < a)
        
          // Old sort
          if(a.key < b.key) return -1;
          if(b.key < a.key) return 1;  
        })
        

        解决方案

        有一些方法可以使该解决方案更短(并且可能更具可读性)——这在代码执行简单任务时很重要。

        首先要注意的是最后一行if(b.key &lt; a.key) return -1 可以缩短为return -1;。这是因为如果 a.key &lt; b.keyb.key = a.key 我们会在前一行返回。

        要注意的第二件事是使用 ES6 语法(可能与旧浏览器不兼容,尤其是对于 Internet Explorer),我们可以使用箭头函数表示法进行回调。

        function(a, b) {} 可以变成(a, b) =&gt; {}

        需要注意的第三点是我们可以转换下面的代码块

        if(a.key < b.key) return -1;
        if(b.key < a.key) return 1;
        

        进入

        return (b.key < a.key) - (a.key < b.key)
        

        这是因为在减法时true 被视为1,而false 被视为0true - false1 - 01false - true0 - 1-10 - 00。永远不会出现true - true出现的情况。

        【讨论】:

        • return (b.key &lt; a.key) - 0.5 不会为相同的键值返回 0。 return (b.key &lt; a.key) - (a.key &lt; b.key); 按照 Array#sort 的预期返回 -1, 0, 1。
        【解决方案8】:

        只需在开头添加一个检查。如果它是 none 对象,则将其移到前面而不执行检查。

        var Comparison_sort = this.Comparison.sort(function (a, b) {
            if (a.key == "None" && a.value == "None")
                return -1;
            if (b.key == "None" && b.value == "None")
                return 1;
            if (a.key < b.key)
                    return -1;
            if (a.key > b.key)
                    return 1;
            return 0;
        });
        

        【讨论】:

        • 这在一般情况下不起作用,例如,None 出现在列表的其他位置。
        • @DaveNewton 数组中的所有元素最终都会作为 a 或 b 传递给排序函数,我看不出它是怎么漏掉的。
        • 将一个项目与其自身进行比较必须返回 0 否则您的排序可能不稳定。
        【解决方案9】:

        如果您希望它位于顶部,您可以返回 -1 或者如果您想要您的项目可以返回 1 >在列表的底部

        对于要从一般排序中排除的第一个或最后一个定位项,您不需要多个 if。

        this.Comparison.sort(function (a, b) {
          if(a.key == b.key) return 0;
          if (a.key == 'None' || b.key == 'None') return -1;
        
          if (a.key < b.key)
              return -1;
          if (a.key > b.key)
              return 1;
          return 0;
        });
        

        【讨论】:

          【解决方案10】:
          const array1 = this.Comparison.filter(e => e.key === 'None');
          const array2 = this.Comparison
                      .filter(e => e.key !== 'None')
                      .sort((a, b) => a.key.localeCompare(b.key));
          
          this.Comparison = [].concat(array1, array2);
          

          【讨论】:

          • 你的答案应该包括一些关于你的方法的cmets。见How to Answer
          【解决方案11】:

          如果您只有一个要放在列表顶部的项目,那么接受的答案很好,但是如果列表需要在开头放置多个项目怎么办?

          以下代码以特定顺序获取一组项目,并确保它们始终在排序后的数组中排在第一位。

          var items = ["P", "Third", "First", "D", "A", "Z", "Second", "Fourth"];
          var topItems = ["First", "Second", "Third", "Fourth"];
          //var topItemsObj = { First: 1, Second: 2, Third: 3, Fourth: 4 };
          
          var mySortFunc = function (a, b) {
              // Add 1 because normally -1 is not found & !0 == true.
              // This could also be replaced by object as !undefined == true. See commented code.
              var 
                  isFirstItem = (v) => topItems.indexOf(v) + 1, 
                  //isFirstItem = (v) => topItemsObj[v] || 0,
                  t1 = isFirstItem(a),
                  t2 = isFirstItem(b);
          
          
              if (t1 && t2) return t1 - t2; // Both items are first items. Sort by array index
              if (t1 || t2) return t2 - t1; // One item is first item. Prioritise first item
              return a.localeCompare(b); // Both items are not first items. Sort normally
          }
          
          items.sort(mySortFunc)
          //  ['First', 'Second', 'Third', 'Fourth', 'A', 'D', 'P', 'Z']
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-02-16
            • 2010-10-22
            • 1970-01-01
            • 2016-04-11
            • 1970-01-01
            • 2021-12-03
            • 1970-01-01
            • 2012-08-11
            相关资源
            最近更新 更多