Array.sort方法停止在Array.length工作> 10

时间:2017-06-15 07:52:03

标签: javascript arrays angular sorting ionic-framework

我正在使用Angular在Ionic的框架上完成一个混合应用程序。我刚刚意识到我的一个功能不起作用。

在我的应用中,用户可以创建项目数据库。这些项是存储在项[]中的对象。有问题的函数首先按名称排序此数组中的对象,然后按类型排序,然后按大小排序。此函数中的逻辑可以正常工作,直到向阵列添加了10个以上的项目。

这是items数组接口:

export interface items {
name: string;
type: string;
size: string;
price: number;
quantity: number;
subTotal: number;
}

这是排序功能:

sort() {

this.items = this.items.sort(function(a, b){
  if(a.name < b.name){
    return -1;
  }
  if(a.name > b.name){
    return 1;
  }
    return 0;
});

this.items = this.items.sort(function(a, b){
  if(a.name == b.name && a.type < b.type){
    return -1;
  }
  if(a.name == b.name && a.type > b.type){
    return 1;
  }
    return 0;
});

this.items = this.items.sort(function(a, b){
  if(a.name == b.name && a.type == b.type && a.size < b.size){
    return -1;
  }
  if(a.name == b.name && a.type == b.type && a.size >  b.size){
    return 1;
  }
    return 0;
});
this.storage.set("items", this.items);
}

我已将问题缩小到包含在此排序函数的最后部分;按大小排序的地方:

this.items = this.items.sort(function(a, b){
    if(a.name == b.name && a.type == b.type && a.size < b.size){
      return -1;
    }
    if(a.name == b.name && a.type == b.type && a.size >  b.size){
      return 1;
    }
      return 0;
  });
  this.storage.set("items", this.items);

请让我知道为什么数组的长度会影响此功能和/或是否有更好的方法。非常感谢你!

3 个答案:

答案 0 :(得分:0)

为什么不使用单一排序方法来处理您希望使用链式方法排序的所有属性,例如

array.sort(function (a, b) {
    return a.name.localeCompare(b.name) || a.type.localeCompare(b.type) || a.size - b.size;
});

首先按名称排序,如果名称相同,则按类型排序,依此类推。

答案 1 :(得分:0)

sort使用different algorithms,具体取决于数组大小。大数组的算法不稳定,这就是为什么你调用sort三次的方法根本不起作用的原因。使用完成整个工作的比较函数调用sort一次:

sort() {
    this.items.sort(function(a, b){
        return (a.name > b.name) - (a.name < b.name)
            || (a.type > b.type) - (a.type < b.type)
            || (a.size > b.size) - (a.size < b.size);
    });
    this.storage.set("items", this.items);
}

答案 2 :(得分:0)

问题的复制

首先,让我们从重现你的问题开始吧。因为你还没有给我们很多工作,我会尝试构建一个&#34;工作空间&#34;这为我们提供了一些数据并清晰地显示了结果。

您实际上在做的是将三种排序方法链接在一起:

  • a属性
  • 排序
  • 如果b属性匹配,则按a属性排序。否则:我不在乎,做你想做的事(返回0
  • 如果c a属性匹配,则按b属性排序。否则:我不在乎,做你想做的事

让我们把这个抽象化并定义一个函数来执行(hacky)&#34;按属性排序&#34;如果给定的一组其他属性匹配:

// req is an array of properties that are required to be equal
// prop is the property name to sort by
// x and y are objects
const yourSort = (req, prop) 
  => (x, y) 
    => req.every(k => x[k] === y[k]) 
      ? x[prop] - y[prop] 
      : 0;

现在,您的方法基本上是这样的:

const aSort = yourSort([], "a");
const bSort = yourSort(["a"], "b");
const cSort = yourSort(["a", "b"], "c");

const chainedSort = arr =>arr
  .sort(aSort)
  .sort(bSort)
  .sort(cSort)

到目前为止,这么好。让我们看看如果我们创建一个包含abc属性的对象列表并继续增加其长度会发生什么:

(注意:我使用淘汰赛来渲染快速用户界面,它与实际逻辑无关)

&#13;
&#13;
const yourSort = (eq, diff) => (x, y) => eq.every(k => x[k] === y[k]) ? x[diff] - y[diff] : 0;

const aSort = yourSort([], "a");
const bSort = yourSort(["a"], "b");
const cSort = yourSort(["a", "b"], "c");

const chainedSort = arr =>arr
  .sort(aSort)
  .sort(bSort)
  .sort(cSort)

// For UI
const length = ko.observable(8);
const sortedItems = ko.pureComputed(
  () => chainedSort(makeItems(Number(length())))
);
ko.applyBindings({ length, sortedItems });

function makeItems(n) { 
  return Array.from(
    Array(n), 
    (_, i) => ({ "a": i % 2, "b": i % 3, "c": Math.random() })
  );
};
&#13;
td { border: 1px solid black; }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

Number of items: <input type="number" data-bind="textInput: length">

<table>
  <thead>
    <th>A</th>
    <th>B</th>
    <th>C</th>  
  </thead>
  <tbody data-bind="foreach: sortedItems">
    <tr>
      <td data-bind="text: a"></td>
      <td data-bind="text: b"></td>
      <td data-bind="text: c"></td>
    </tr>
  </tbody>
</table>
&#13;
&#13;
&#13;

现在,在我的浏览器(最新的Chrome)中,您可以看到length > 10的所有内容都搞砸了,但在其他浏览器中,某些内容甚至可能会更早破坏!

显然,浏览器从一个稳定的排序切换,其中return 0表示&#34;保留两个项目的初始顺序,我将完整的&#34;比较为不稳定的排序, return 0表示&#34;你可以做任何你想做的事情&#34;。

解决问题

我在评论和其他答案中发布的解决此问题的方法是构建一个比较方法,该方法考虑所有属性。您可以像其他答案建议的那样手动执行此操作,或使用返回组合分类器的辅助函数。我选择后者,但在返回value1 - value2时采用了一个捷径,这只适用于数值。

现在,您将看到任何长度的正确排序。

&#13;
&#13;
const sortByProps = props => (obj1, obj2) => {
  for (let i = 0; i < props.length; i += 1) {
    const k = props[i];
    if (obj1[k] - obj2[k] === 0) continue;
    return obj1[k] - obj2[k];
  }
  
  return 0;  
}

const combinedSort = arr => arr.sort(sortByProps(["a", "b", "c"]));

// For UI
const length = ko.observable(8);
const sortedItems = ko.pureComputed(
  () => combinedSort(makeItems(Number(length())))
);
ko.applyBindings({ length, sortedItems });

function makeItems(n) { 
  return Array.from(
    Array(n), 
    (_, i) => ({ "a": i % 2, "b": i % 3, "c": Math.random() })
  );
};
&#13;
td { border: 1px solid black; }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

Number of items: <input type="number" data-bind="textInput: length">

<table>
  <thead>
    <th>A</th>
    <th>B</th>
    <th>C</th>  
  </thead>
  <tbody data-bind="foreach: sortedItems">
    <tr>
      <td data-bind="text: a"></td>
      <td data-bind="text: b"></td>
      <td data-bind="text: c"></td>
    </tr>
  </tbody>
</table>
&#13;
&#13;
&#13;