javascript檢查物件是否存在 - javascript陣列distinct




如何檢查數組是否包含JavaScript中的對象? (20)

ECMAScript 6有一個關於find的優雅提議。

find方法對數組中存在的每個元素執行一次回調函數,直到找到一個回調返回true值的元素。 如果找到這樣的元素,find會立即返回該元素的值。 否則,find返回undefined。 僅對已分配值的數組的索引調用回調; 對於已刪除的索引或從未分配過值的索引,不會調用它。

這是關於它的MDN文檔

find功能就像這樣。

function isPrime(element, index, array) {
    var start = 2;
    while (start <= Math.sqrt(element)) {
        if (element % start++ < 1) return false;
    }
    return (element > 1);
}

console.log( [4, 6, 8, 12].find(isPrime) ); // Undefined, not found
console.log( [4, 5, 8, 12].find(isPrime) ); // 5

您可以通過定義函數在ECMAScript 5及更低版本中使用它。

if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(predicate) {
      if (this == null) {
        throw new TypeError('Array.prototype.find called on null or undefined');
      }
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }
      var list = Object(this);
      var length = list.length >>> 0;
      var thisArg = arguments[1];
      var value;

      for (var i = 0; i < length; i++) {
        if (i in list) {
          value = list[i];
          if (predicate.call(thisArg, value, i, list)) {
            return value;
          }
        }
      }
      return undefined;
    }
  });
}

找出JavaScript數組是否包含對象的最簡潔有效的方法是什麼?

這是我知道的唯一方法:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (a[i] === obj) {
            return true;
        }
    }
    return false;
}

有沒有更好,更簡潔的方法來實現這一目標?

這與Stack Overflow問題密切相關問題在JavaScript數組中查找項目的最佳方法是什麼? 它使用indexOf尋址數組中的對象。


ECMAScript 7引入了array.includes(value)

它可以像這樣使用:

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false

它還接受fromIndex的可選第二個參數:

[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true

與使用Strict Equality Comparison的 indexOf不同,它includes使用SameValueZero相等算法的比較。 這意味著您可以檢測陣列是否包含NaN

[1, 2, NaN].includes(NaN); // true

indexOf不同, includes不會跳過缺失的索引:

new Array(5).includes(undefined); // true

目前它仍然是草稿,但可以polyfill以使其適用於所有瀏覽器。


b是值, a是數組。 它返回truefalse

function(a, b) {
    return a.indexOf(b) != -1
}

希望更快的雙向indexOf / lastIndexOf替代方案

2015年

雖然新方法array.includes(value)非常好,但現在支持基本上為零。

我一直想著替換慢的indexOf / lastIndexOf函數。

已找到一種高效的方式,查看最佳答案。 從那些我選擇@Damir Zekic發布的contains函數的那個應該是最快的。 但它也指出基準是從2008年開始的,因此已經過時了。

我也喜歡while結束for ,但由於沒有特定的原因,我結束了用for循環編寫函數。 它也可以用一段while --

如果我在做這個時檢查數組的兩側,迭代速度要慢得多,我很好奇。 顯然不是,因此這個功能比最高投票的功能快兩倍左右。 顯然它也比原生的快。 這在現實環境中,您永遠不知道您正在搜索的值是在數組的開頭還是結尾。

當你知道你剛推送一個帶有值的數組時,使用lastIndexOf仍然可能是最好的解決方案,但是如果你必須通過大數組並且結果可能無處不在,那麼這可能是一個可以讓事情變得更快的可靠解決方案。

雙向indexOf / lastIndexOf

function bidirectionalIndexOf(a, b, c, d, e){
  for(c=a.length,d=c*1; c--; ){
    if(a[c]==b) return c; //or this[c]===b
    if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
  }
  return -1
}

//Usage
bidirectionalIndexOf(array,'value');

性能測試

http://jsperf.com/bidirectionalindexof

作為測試,我創建了一個包含100k條目的數組。

三個查詢:在數組的開頭,中間和末尾。

我希望你也發現這很有趣並測試性能。

注意:正如您所看到的,我稍微修改了contains函數以反映indexOf和lastIndexOf輸出(因此對index基本上都是true ,而對於-1基本為false )。 這不應該傷害它。

陣列原型變體

Object.defineProperty(Array.prototype,'bidirectionalIndexOf',{value:function(b,c,d,e){
  for(c=this.length,d=c*1; c--; ){
    if(this[c]==b) return c; //or this[c]===b
    if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
  }
  return -1
},writable:false, enumerable:false});

// Usage
array.bidirectionalIndexOf('value');

該函數也可以很容易地修改為返回true或false,甚至是對象,字符串或其他任何東西。

這是while變體:

function bidirectionalIndexOf(a, b, c, d){
  c=a.length; d=c-1;
  while(c--){
    if(b===a[c]) return c;
    if(b===a[d-c]) return d-c;
  }
  return c
}

// Usage
bidirectionalIndexOf(array,'value');

這怎麼可能?

我認為在數組中獲取反射索引的簡單計算非常簡單,它比實際循環迭代快兩倍。

下面是一個複雜的示例,每次迭代執行三次檢查,但這只能通過更長的計算才能實現,這會導致代碼速度變慢。

http://jsperf.com/bidirectionalindexof/2


一內膽:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}

以下是Prototype的用法

/**
 *  Array#indexOf(item[, offset = 0]) -> Number
 *  - item (?): A value that may or may not be in the array.
 *  - offset (Number): The number of initial items to skip before beginning the
 *      search.
 *
 *  Returns the position of the first occurrence of `item` within the array &mdash; or
 *  `-1` if `item` doesn't exist in the array.
**/
function indexOf(item, i) {
  i || (i = 0);
  var length = this.length;
  if (i < 0) i = length + i;
  for (; i < length; i++)
    if (this[i] === item) return i;
  return -1;
}

另見here他們如何勾選它。


使用lodash的some功能。

它簡潔,準確,並具有很好的跨平台支持。

接受的答案甚至不符合要求。

要求:推薦最簡潔有效的方法來確定JavaScript數組是否包含對象。

一般承認的答案:

$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
> -1

我的建議:

_.some([{'a': 1}, {'b': 2}], {'b': 2})
> true

筆記:

$ .inArray可以很好地確定標量數組中是否存在量值...

$.inArray(2, [1,2])
> 1

...但問題明確要求確定對是否包含在數組中的有效方法。

為了處理標量和對象,您可以這樣做:

(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)

使用:

Array.prototype.contains = function(x){
  var retVal = -1;

  // x is a primitive type
  if(["string","number"].indexOf(typeof x)>=0 ){ retVal = this.indexOf(x);}

  // x is a function
  else if(typeof x =="function") for(var ix in this){
    if((this[ix]+"")==(x+"")) retVal = ix;
  }

  //x is an object...
  else {
    var sx=JSON.stringify(x);
    for(var ix in this){
      if(typeof this[ix] =="object" && JSON.stringify(this[ix])==sx) retVal = ix;
    }
  }

  //Return False if -1 else number if numeric otherwise string
  return (retVal === -1)?false : ( isNaN(+retVal) ? retVal : +retVal);
}

我知道這不是最好的方法,但由於沒有本地IComparable方法在對象之間進行交互,我想這就像你可以比較一個數組中的兩個實體一樣接近。 此外,擴展Array對象可能不是一件明智的事情,但有時候它是可以的(如果你知道它和權衡)。



可以使用具有方法“has()”的Set

function contains(arr, obj) {
  var proxy = new Set(arr);
  if (proxy.has(obj))
    return true;
  else
    return false;
}

var arr = ['Happy', 'New', 'Year'];
console.log(contains(arr, 'Happy'));

如果您使用的是JavaScript 1.6或更高版本(Firefox 1.5或更高版本),則可以使用Array.indexOf 。 否則,我認為你最終會得到類似於原始代碼的東西。


如果您反複檢查數組中是否存在對象,您應該查看

  1. 通過在數組中執行插入排序來保持數組始終排序 (將新對象放在正確的位置)
  2. 將對象更新為刪除+排序插入操作和
  3. contains(a, obj)使用二進制搜索查找。

我們使用此代碼段(適用於對象,數組,字符串):

/*
 * @function
 * @name Object.prototype.inArray
 * @description Extend Object prototype within inArray function
 *
 * @param {mix}    needle       - Search-able needle
 * @param {bool}   searchInKey  - Search needle in keys?
 *
 */
Object.defineProperty(Object.prototype, 'inArray',{
    value: function(needle, searchInKey){

        var object = this;

        if( Object.prototype.toString.call(needle) === '[object Object]' || 
            Object.prototype.toString.call(needle) === '[object Array]'){
            needle = JSON.stringify(needle);
        }

        return Object.keys(object).some(function(key){

            var value = object[key];

            if( Object.prototype.toString.call(value) === '[object Object]' || 
                Object.prototype.toString.call(value) === '[object Array]'){
                value = JSON.stringify(value);
            }

            if(searchInKey){
                if(value === needle || key === needle){
                return true;
                }
            }else{
                if(value === needle){
                    return true;
                }
            }
        });
    },
    writable: true,
    configurable: true,
    enumerable: false
});

用法:

var a = {one: "first", two: "second", foo: {three: "third"}};
a.inArray("first");          //true
a.inArray("foo");            //false
a.inArray("foo", true);      //true - search by keys
a.inArray({three: "third"}); //true

var b = ["one", "two", "three", "four", {foo: 'val'}];
b.inArray("one");         //true
b.inArray('foo');         //false
b.inArray({foo: 'val'})   //true
b.inArray("{foo: 'val'}") //false

var c = "String";
c.inArray("S");        //true
c.inArray("s");        //false
c.inArray("2", true);  //true
c.inArray("20", true); //false

擴展JavaScript Array對像是一個非常糟糕的主意,因為您將新屬性(您的自定義方法)引入到可能破壞現有腳本的for-in循環中。 幾年前, Prototype庫的作者不得不重新設計他們的庫實現來刪除這種東西。

如果您不需要擔心與頁面上運行的其他JavaScript的兼容性,那就去吧,否則,我會推薦更笨拙但更安全的獨立功能解決方案。


正如其他人所提到的,您可以使用Array.indexOf ,但並非所有瀏覽器都可以使用它。 以下是來自https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf的代碼,以使其在舊版瀏覽器中的工作方式相同。

indexOf是ECMA-262標準的最新成員; 因此,它可能不會出現在所有瀏覽器中。 您可以通過在腳本的開頭插入以下代碼來解決此問題,允許在本身不支持它的實現中使用indexOf。 該算法正好是ECMA-262第5版中指定的算法,假設Object,TypeError,Number,Math.floor,Math.abs和Math.max具有其原始值。

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
        "use strict";
        if (this == null) {
            throw new TypeError();
        }
        var t = Object(this);
        var len = t.length >>> 0;
        if (len === 0) {
            return -1;
        }
        var n = 0;
        if (arguments.length > 1) {
            n = Number(arguments[1]);
            if (n != n) { // shortcut for verifying if it's NaN
                n = 0;
            } else if (n != 0 && n != Infinity && n != -Infinity) {
                n = (n > 0 || -1) * Math.floor(Math.abs(n));
            }
        }
        if (n >= len) {
            return -1;
        }
        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
        for (; k < len; k++) {
            if (k in t && t[k] === searchElement) {
                return k;
            }
        }
        return -1;
    }
}

當前的瀏覽器具有Array#includes ,這正是如此, 得到了廣泛支持 ,並且具有適用於舊瀏覽器的polyfill

> ['joe', 'jane', 'mary'].includes('jane');
true 

您也可以使用不那麼直接的Array#indexOf ,但不要求Polyfills用於過時的瀏覽器。

jQuery提供$.inArray ,它在功能上等同於Array#indexOf

underscore.js是一個JavaScript實用程序庫,提供_.contains(list, value) ,別名_.include(list, value) ,如果傳遞了JavaScript數組,它們都在內部使用indexOf

其他一些框架提供了類似的方法:

請注意,某些框架將此實現為函數,而其他框架則將該函數添加到數組原型中。


這是Array.indexOfJavaScript 1.6兼容實現:

if (!Array.indexOf)
{
  Array.indexOf = [].indexOf ?
      function (arr, obj, from) { return arr.indexOf(obj, from); }:
      function (arr, obj, from) { // (for IE6)
        var l = arr.length,
            i = from ? parseInt( (1*from) + (from<0 ? l:0), 10) : 0;
        i = i<0 ? 0 : i;
        for (; i<l; i++) {
          if (i in arr  &&  arr[i] === obj) { return i; }
        }
        return -1;
      };
}

適用於所有現代瀏覽器的解決方案:

function contains(arr, obj) {
  const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration
  return arr.some(item => JSON.stringify(item) === stringifiedObj);
}

用法:

contains([{a: 1}, {a: 2}], {a: 1}); // true

IE6 +解決方案:

function contains(arr, obj) {
  var stringifiedObj = JSON.stringify(obj)
  return arr.some(function (item) {
    return JSON.stringify(item) === stringifiedObj;
  });
}

// .some polyfill, not needed for IE9+
if (!('some' in Array.prototype)) {
  Array.prototype.some = function (tester, that /*opt*/) {
    for (var i = 0, n = this.length; i < n; i++) {
      if (i in this && tester.call(that, this[i], i, this)) return true;
    } return false;
  };
}

用法:

contains([{a: 1}, {a: 2}], {a: 1}); // true

為什麼要使用JSON.stringify

Array.indexOfArray.includes (以及此處的大多數答案)僅通過引用而不是按值進行比較。

[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object

獎金

非優化的ES6單線程:

[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
// true

注意:如果按鍵的順序相同,按值比較對象會更好,所以為了安全起見,您可以先使用類似這樣的包對鍵進行排序: https://www.npmjs.com/package/sort-keyshttps://www.npmjs.com/package/sort-keys

使用perf優化更新了contains函數。 感謝itinance指出它。


雖然array.indexOf(x)!=-1是最簡潔的方法(並且已經被非Internet Explorer瀏覽器支持了十多年......),但它不是O(1),而是O( N),這太可怕了。 如果您的數組不會更改,您可以將數組轉換為哈希表,然後執行table[x]!==undefined===undefined

Array.prototype.toTable = function() {
    var t = {};
    this.forEach(function(x){t[x]=true});
    return t;
}

演示:

var toRemove = [2,4].toTable();
[1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})

(不幸的是,雖然你可以創建一個Array.prototype.contains來“凍結”一個數組並將this._cache中的哈希表存儲在兩行中,但是如果你以後選擇編輯你的數組,這會產生錯誤的結果.JavaScript沒有足夠的鉤子。讓你保持這種狀態,例如Python不同。)


function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some()已添加到第5版的ECMA-262標準中





browser