如何在排序的索引位置将元素推入数组?

时间:2017-03-22 08:44:50

标签: javascript arrays

假设我已经有这些文章的数组,按最低价格到最高价格排序:

[
  { title: "Article 3", price: 1.49 },
  { title: "Article 1", price: 3.00 },
  { title: "Article 4", price: 5.99 },
  { title: "Article 2", price: 19.99 }
]

基本上我想将另一篇文章推送到数组中的正确位置(按价格)。我怎么能这样做?

我推送的新文章可能具有以下属性:

{ title: "Article 5", price: 12.00 }

我希望文章出现在索引3(第4条​​和第2条之间)。

更新(解决方案)

我使用@ klutt的二元搜索算法答案创建了一个原型方法:

Array.prototype.pushSorted = function(el, compareFn) {
  this.splice((function(arr) {
    var m = 0;
    var n = arr.length - 1;

    while(m <= n) {
      var k = (n + m) >> 1;
      var cmp = compareFn(el, arr[k]);

      if(cmp > 0) m = k + 1;
        else if(cmp < 0) n = k - 1;
        else return k;
    }

    return -m - 1;
  })(this), 0, el);

  return this.length;
};

const sortByPrice = (a, b) => a.price > b.price;
theArray.pushSorted({ title: "Article 5", price: 12.00 }, sortByPrice);

8 个答案:

答案 0 :(得分:2)

为了避免每次都对数组进行排序,您可以遍历数组,直到找到价格较高的元素,然后使用array.splice(index, 0, element)将元素插入正确的位置:

&#13;
&#13;
var array = [
  { title: "Article 3", price: 1.49 },
  { title: "Article 1", price: 3.00 },
  { title: "Article 4", price: 5.99 },
  { title: "Article 2", price: 19.99 }
]

function insertSorted (array, element, comparator) {
  for (var i = 0; i < array.length && comparator(array[i], element) < 0; i++) {}
  array.splice(i, 0, element)
}

function compareByPrice (a, b) { return a.price - b.price }

insertSorted(array, { title: "Article 5", price: 12.00 }, compareByPrice)

console.log(array)
&#13;
&#13;
&#13;

答案 1 :(得分:2)

如果它是一个很长的列表,您不希望每次都对列表​​进行排序。使用浮点数的排序最多为O(n * log n)。如果进行线性搜索,则会改为使用O(n)。

function search(a, v) {
    if(a[0]['price'] > v['price']) {
        return 0;
    }
    var i=1;
    while (i<a.length && !(a[i]['price'] > v['price'] && a[i-1]['price'] <= v['price'])) {
        i=i+1;
        }
    return i;
}

myArray.splice(search(myArray, newItem), 0, newItem)

但是,如果列表很长,那么进行二分搜索而不是线性搜索是个好主意。这将把它降低到O(log n)。二进制搜索非常简单。你从中间开始。如果元素大于您要搜索的元素,请将相同的算法应用于列表的上半部分,否则应用于下部。在网上找到二进制搜索的代码示例很容易。

以下是一个例子:

function binarySearch(ar, el, compare_fn) {
    if (el.price < ar[0].price)
        return 0;
    if (el.price > ar[ar.length-1].price)
        return ar.length;
    var m = 0;
    var n = ar.length - 1;
    while (m <= n) {
        var k = (n + m) >> 1;
        var cmp = compare_fn(el, ar[k]);
        if (cmp > 0) {
            m = k + 1;
        } else if(cmp < 0) {
            n = k - 1;
        } else {
            return k;
        }
    }
    return -m - 1;
}

function comp(a, b) {
    return a['price']>b['price']
}

myArray.splice(binarySearch(myArray, element, comp), 0, element)

(从这里偷来Binary Search in Javascript

但要把它包起来。添加元素然后排序通常是个坏主意。最好的情况是,它没关系,但既然你知道列表是排序的,为什么不至少做一个线性搜索呢?

如果列表很小,这没关系,但如果列表中有数百万个元素,那么差异就会非常明显。

编辑:

我做了一个快速而原始的基准。

            10,000   100,000  1000,000   10,000,000  
Sort            80       900     13000          N/A
Linear           2         2        25         5000
Binary           2         2         5           21

我测量了在四种不同尺寸上运行三种算法所花费的时间。我不想等待排序结束千万个元素。因此N / A.时间以毫秒为单位。请注意,基准测试非常原始,但它可以了解尺寸增长时它会产生多大影响。

答案 2 :(得分:1)

只需推送您的商品

var myArray = [
  { title: "Article 3", price: 1.49 },
  { title: "Article 1", price: 3.00 },
  { title: "Article 4", price: 5.99 },
  { title: "Article 2", price: 19.99 }
]

myArray.push({ title: "Article 5", price: 12.00 })

然后排序你的数组:

myArray.sort(function(a, b) {
    return parseFloat(a.price) - parseFloat(b.price);
});

答案 3 :(得分:1)

我通过调整代码在Angular中实现了这一点。有人会觉得有用。

Array.prototype.sortedInsert = sortedInsert;

interface Array<T> {
    sortedInsert(element: T, comparator: any): number;
}

/**
 *  This function takes an element, and a comparator function.
 *  It returns position of the inserted record.
 */

function sortedInsert<T>(element: T, comparatorFn: any): number {
    const insertionIndex: number = findInsertionIndex(this, element, comparatorFn);
    this.splice(insertionIndex, 0, element);
    return insertionIndex;
}

function findInsertionIndex<T>(arr: Array<T>, element: T, comparatorFn: any): number {
    if (arr.length === 0) {
        return 0;
    }

    let low = 0;
    let high: number = arr.length - 1;

    while (low <= high) {
        const mid: number = Math.floor(low + (high - low) / 2);
        const comparison: number = comparatorFn(element, arr[mid]);
        if (comparison === 0) {
            return mid;
        } else if (comparison < 0) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return low;
}

要使用此文件,您必须将此文件导入app.module.ts
用法示例:

const arr = [];
const comp = (a: number, b: number) => a - b;
arr.sortedInsert(2, comp);
arr.sortedInsert(1, comp);
arr.sortedInsert(3, comp);
console.log(arr);

答案 4 :(得分:0)

Update (Solution)仍然无法正常运行。应该放在开头或结尾的项目放置在错误的位置,请参阅基于相同代码的修订版解决方案:

Array.prototype.pushSorted = function(el, compareFn) {
  let index = (function(arr) {
    var m = 0;
    var n = arr.length - 1;

    while(m <= n) {
      var k = (n + m) >> 1;
      var cmp = compareFn(el, arr[k]);

      if(cmp > 0) m = k + 1;
        else if(cmp < 0) n = k - 1;
        else {
          console.log(`m=${m} k=${k}`)
          return k;
        };
    }


    return -m - 1;
  })(this);

  if (index >= 0)
    this.splice(index, 0, el);
  else if (index < 0)
    this.splice((index * -1) - 1, 0, el);

  return this.length;
};

样品用量:

a = [1, 2, 3, 4, 5]
a.pushSorted(2.5, (a,b) => a - b);
a.pushSorted(8, (a,b) => a - b);
a.pushSorted(0.5, (a,b) => a - b);

答案 5 :(得分:0)

这是我当前的打字稿实现:

enum SortDirection {
  Ascending,
  Descending
}

const propertyComparer = <T>(
  a: T,
  b: T,
  property: keyof T,
  direction: SortDirection
): number => {
  if (a[property] < b[property]) {
    return direction === SortDirection.Ascending ? 1 : -1;
  }
  if (a[property] > b[property]) {
    return direction === SortDirection.Ascending ? -1 : 1;
  }
  return 0;
};

const findAndInsertByProperty = <T>(
  arr: T[],
  item: T,
  property: keyof T,
  direction: SortDirection
): number => {
  let start = 0;
  let end = arr.length;

  while (start < end) {
    const mid = (start + end) >> 1;
    const result = propertyComparer<T>(item, arr[mid], property, direction);

    if (result === 0) {
      return mid;
    } else if (result < 0) {
      end = mid;
    } else {
      start = mid + 1;
    }
  }

  return end;
};

const myArray = [
  { title: "Article 3", price: 1.49 },
  { title: "Article 1", price: 3.00 },
  { title: "Article 4", price: 5.99 },
  { title: "Article 2", price: 19.99 }
];
const value = { title: "Article 5", price: 12.00 };
const valueLowest = { title: "Article 6", price: 0.49 };
const valueHighest = { title: "Article 7", price: 20.00 };

myArray.splice(findAndInsertByProperty(myArray, value, 'price', SortDirection.Descending), 0, value);
myArray.splice(findAndInsertByProperty(myArray, valueLowest, 'price', SortDirection.Descending), 0, valueLowest);
myArray.splice(findAndInsertByProperty(myArray, valueHighest, 'price', SortDirection.Descending), 0, valueHighest);

console.log(myArray);

答案 6 :(得分:-1)

不确定这是否是最佳的,但您可以将其推入另一个(重复的)数组,然后对其进行排序,然后检查其索引以便将其推送到正确的索引位置:

Array.prototype.pushSorted = function(value, compareFunction) {
  const arrCopy = this.map(element => element);

  arrCopy.push(value);
  arrCopy.sort(compareFunction);
  this.splice(arrCopy.indexOf(value), 0, value);

  return this.length;
};

首先,我们创建一个数组实例的副本。然后将元素推入此数组副本。然后对数组副本进行排序。然后将元素推送(通过splice方法)到实数数组中,同时检查数组副本中的索引。然后我们可以返回数组长度(就像普通的push)。

示例:

const sortByPrice = (a, b) => a > b ? 1 : -1;
const newArticle = { title: "Article 5", price: 12.00 };

theArray.pushSorted(newArticle, sortByPrice);

答案 7 :(得分:-1)

考虑我的解决方案如下;

var articles=[
  { title: "Article 3", price: 1.49 },
  { title: "Article 1", price: 3.00 },
  { title: "Article 4", price: 5.99 },
  { title: "Article 2", price: 19.99 }
]

现在以这种方式写一个函数;

function sortByKey(array, key) {
    return array.sort(function(a, b) {
        var x = a[key]; var y = b[key];
       return ((x < y) ? -1 : ((x > y) ? 1 : 0));
   });
}

现在插入一个元素;

articles.push({ title: "Article 5", price: 12.00 });

最后排序;

articles=sortByKey(articles, 'price');