Phương thức indexOf trong một mảng đối tượng?


514

Phương pháp tốt nhất để lấy chỉ mục của một mảng chứa các đối tượng là gì?

Hãy tưởng tượng kịch bản này:

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

Bây giờ tôi muốn có indexOfđối tượng thuộc hellotính 'stevie'nào, trong ví dụ này, sẽ là 1.

Tôi là một người mới làm quen với JavaScript và tôi không biết liệu có một phương pháp đơn giản hay tôi nên xây dựng chức năng của riêng mình để làm điều đó.


1
Bạn có muốn hợp nhất hai đối tượng helloqaz?
Armin

Không, tôi không. Tôi muốn có một danh sách các đối tượng trong một mảng.
Antonio Laguna

À được rồi! Bạn muốn biết vị trí của toàn bộ đối tượng trong mảng, có thuộc tính được xác định.
Armin

10
Tôi đã tìm thấy một hàm rất đơn giản để giải quyết vấn đề chính xác này với câu trả lời SO này: var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor); var objectFound = array[elementPos]; [link] ( stackoverflow.com/a/16100446/1937255 )
Rick

ES6 Array.indexOf tốt hơn câu trả lời được chấp nhận (nếu ES6 phù hợp với bạn) - xem ví dụ đầy đủ bên dưới
yar1

Câu trả lời:


1044

Tôi nghĩ bạn có thể giải quyết nó trong một dòng bằng chức năng bản đồ :

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

58
Điều này nên thành thật là câu trả lời được chấp nhận. Hầu hết các trình duyệt hiện nay đều hỗ trợArray.prototype.map()
AlbertEngelB

10
Nó không được IE8 hỗ trợ nhưng, nếu đó không phải là vấn đề thì đây là giải pháp tốt nhất.
Antonio Laguna

57
Ừm ... không có gì đáng chú ý khi Array.prototype.map () tạo ra một mảng hoàn toàn mới chứa các mục được ánh xạ? Vậy, nếu bạn đã có một mảng có 1000 phần tử, trước tiên bạn đã tạo một mảng khác có 1000 phần tử, sau đó tìm kiếm nó? Sẽ là đáng giá khi tôi nghĩ rằng để xem hiệu suất của phương pháp này so với một vòng lặp đơn giản. Đặc biệt là khi bạn đang chạy trên một nền tảng di động với nguồn lực hạn chế.
Doug

7
@Doug Mặc dù quan điểm của bạn về hiệu suất thực sự là chính xác, nhưng ai là người có suy nghĩ đúng đắn sẽ thay thế một dòng mã bằng bảy cho một ứng dụng gần như theo định nghĩa IO / Network bị ràng buộc cho đến khi chúng được cấu hình cho các tắc nghẽn?
Jared Smith

6
Về mặt kỹ thuật, một tệp js
rút gọn

371

Array.prototype.findIndex được hỗ trợ trong tất cả các trình duyệt khác ngoài IE (không có cạnh). Nhưng polyfill cung cấp là tốt đẹp.

var indexOfStevie = myArray.findIndex(i => i.hello === "stevie");

Giải pháp với bản đồ là được. Nhưng bạn đang lặp đi lặp lại trên toàn bộ mảng mỗi tìm kiếm. Đó chỉ là trường hợp xấu nhất đối với find Index dừng lặp lại khi tìm thấy kết quả khớp.


Không thực sự là một cách ngắn gọn (khi các nhà phát triển phải lo lắng về IE8) , nhưng đây là một giải pháp phổ biến:

var searchTerm = "stevie",
    index = -1;
for(var i = 0, len = myArray.length; i < len; i++) {
    if (myArray[i].hello === searchTerm) {
        index = i;
        break;
    }
}

hoặc như một chức năng:

function arrayObjectIndexOf(myArray, searchTerm, property) {
    for(var i = 0, len = myArray.length; i < len; i++) {
        if (myArray[i][property] === searchTerm) return i;
    }
    return -1;
}
arrayObjectIndexOf(arr, "stevie", "hello"); // 1

Chỉ cần một số lưu ý:

  1. Không sử dụng cho ... trong các vòng lặp trên mảng
  2. Hãy chắc chắn thoát ra khỏi vòng lặp hoặc quay trở lại chức năng một khi bạn đã tìm thấy "kim" của mình
  3. Cẩn thận với sự bình đẳng đối tượng

Ví dụ,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found

chức năng có sự so sánh tìm kiếm sai vì nó phải là searchTerm :)
Antonio Laguna

có nhiều sự cố xảy ra
Joe

3
@SteveBennett nó là một phiên bản tối ưu hóa hiệu suất; độ dài của mảng chỉ được xác định một lần (khi các biến cho vòng lặp for được khởi tạo). Trong trường hợp của bạn, độ dài được kiểm tra mỗi lần lặp lại một lần nữa. Xem thêm stackoverflow.com/questions/5349425/ và và stackoverflow.com/questions/8452317/ Tuy nhiên, nếu hiệu suất không cao thì điều đó không thực sự quan trọng.
lo ngại

1
Câu trả lời tốt, nhưng tôi đã thực hiện một số điểm chuẩn hiệu suất (xem jsperf.com/find-index-of-object-in-array-by-contents ), và thấy rằng câu trả lời dựa trên chức năng được đề cập ở đây dường như là câu trả lời hiệu quả nhất thứ hai. Điều duy nhất nhiều người biểu diễn cuối cùng đã đưa nó vào một nguyên mẫu, thay vì chỉ là một chức năng, như được đề cập trong câu trả lời của tôi .
Uniphonic

123

Trong ES2015, điều này khá dễ dàng:

myArray.map(x => x.hello).indexOf('stevie')

hoặc, có lẽ với hiệu suất tốt hơn cho các mảng lớn hơn:

myArray.findIndex(x => x.hello === 'stevie')

2
Cách tiếp cận tốt để sử dụng ES6
kag

Tôi đã ngạc nhiên rằng cả hai phương pháp này đều không hiệu quả như nguyên mẫu cho vòng lặp, như đã đề cập trong câu trả lời của tôi. Mặc dù hỗ trợ trình duyệt phương thức find Index hơi kém, nhưng có vẻ như nó sẽ làm điều tương tự, nhưng cuối cùng vẫn ít hiệu suất hơn? Xem liên kết trong câu trả lời của tôi cho điểm chuẩn.
Uniphonic

Điều tốt để biết, nếu hiệu suất là quan trọng cho nhiệm vụ trong tầm tay. Theo kinh nghiệm của tôi, nó rất hiếm khi, nhưng ymmv.
Steve Bennett

24
var idx = myArray.reduce( function( cur, val, index ){

    if( val.hello === "stevie" && cur === -1 ) {
        return index;
    }
    return cur;

}, -1 );

17

Tôi thích câu trả lời của Pablo, nhưng bản đồ Array # indexOf và Array # không hoạt động trên tất cả các trình duyệt. Gạch dưới sẽ sử dụng mã gốc nếu nó có sẵn, nhưng cũng có dự phòng. Thêm vào đó, nó có phương pháp gảy để thực hiện chính xác những gì phương pháp bản đồ ẩn danh của Pablo làm.

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();

1
Array.prototype.map () được hỗ trợ cho IE9 + và bạn có thể sử dụng Polyfill cho IE8, 7, 6: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Johann Echavarria

1
Bạn có thể sử dụng một polyfill. . . hoặc bạn chỉ có thể sử dụng gạch dưới hoặc lodash, về cơ bản là các polyfill có cả một loạt các tính năng khác kèm theo . Những gì phản đối với gạch dưới? Kích thước?
tandrewnichols

Tôi thực sự thích Underscore, câu trả lời của bạn cũng rất thông minh, nhưng câu trả lời của IMHO Pablo là sạch nhất.
Johann Echavarria

Wow tôi chưa bao giờ nghĩ sẽ sử dụng chuỗi như thế. Tôi thực sự thích cách nó làm cho việc tìm kiếm trôi chảy.
Dylan Pierce

chainlà không cần thiết ở đây. _.pluck(myArray, 'hello').indexOf('stevie')
Steve Bennett

14

Hoặc nguyên mẫu nó:

Array.prototype.indexOfObject = function arrayObjectIndexOf(property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArr.indexOfObject("name", "stevie");

9
Rât thuận tiện! Mặc dù tôi sẽ chọn nguyên mẫu.indexOfObject để không can thiệp vào phương thức exisitng Array.indexOf. Array.prototype.indexOfObject = function(property, value) { for (var i = 0, len = this.length; i < len; i++) { if (this[i][property] === value) return i; } return -1; };
Adam

1
Tôi sẽ gói nó trong một bao đóng tự thực hiện với cái cũ được lưu trữ trước đó, với dòng đầu tiên của chức năng thay thế là một cái gì đó dọc theo dòng if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);. Điều này là do đây là một vài loại là bất biến. Tôi cũng sẽ có một đối số thứ ba để kích hoạt dự phòng cho phương thức gốc nếu cần.
Isiah Meadows

8

Tóm tắt

myArray.indexOf('stevie','hello')

Trường hợp sử dụng :

  /*****NORMAL****/  
[2,4,5].indexOf(4) ;//OUTPUT 1
 /****COMPLEX*****/
 [{slm:2},{slm:4},{slm:5}].indexOf(4,'slm');//OUTPUT 1
 //OR
 [{slm:2},{slm:4},{slm:5}].indexOf(4,function(e,i){
   return e.slm;
});//OUTPUT 1
/***MORE Complex**/
[{slm:{salat:2}},{slm:{salat:4}},{slm:{salat:5}}].indexOf(4,function(e,i){
   return e.slm.salat;
});//OUTPUT 1

API:

    Array.prototype.indexOfOld=Array.prototype.indexOf

    Array.prototype.indexOf=function(e,fn){
      if(!fn){return this.indexOfOld(e)}
      else{ 
       if(typeof fn ==='string'){var att=fn;fn=function(e){return e[att];}}
        return this.map(fn).indexOfOld(e);
      }
    };

6

Tôi đã thực hiện một số thử nghiệm hiệu suất của các câu trả lời khác nhau ở đây, mà bất kỳ ai cũng có thể tự chạy chúng:

https://jsperf.com/find-index-of-object-in-array-by-contents

Dựa trên các thử nghiệm ban đầu của tôi trong Chrome, phương pháp sau (sử dụng vòng lặp for được thiết lập bên trong nguyên mẫu) là nhanh nhất:

Array.prototype.indexOfObject = function (property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArray.indexOfObject("hello", "stevie");

Mã này là một phiên bản sửa đổi một chút của câu trả lời của Nathan Zaetta.

Trong các điểm chuẩn hiệu năng, tôi đã thử nó với cả mục tiêu ở giữa (chỉ số 500) và cuối (chỉ số 999) của mảng 1000 đối tượng và ngay cả khi tôi đặt mục tiêu là mục cuối cùng trong mảng (nghĩa là rằng nó phải lặp qua từng mục duy nhất trong mảng trước khi tìm thấy) nó vẫn kết thúc nhanh nhất.

Giải pháp này cũng có lợi ích là một trong những điều ngắn gọn nhất để thực hiện nhiều lần, vì chỉ dòng cuối cùng cần phải được lặp lại:

myArray.indexOfObject("hello", "stevie");

2
Tôi chỉ định đăng một câu trả lời cho câu hỏi này bằng cách sử dụng một câu đố với các bài kiểm tra của riêng tôi, nhưng nhờ câu trả lời của bạn, tôi không phải làm thế nữa. Tôi chỉ muốn xác nhận các bài kiểm tra của bạn - Tôi đã nhận được kết quả tương tự, nhưng sử dụng một whilevòng lặp thay vì formột, và performance.now(). Tôi ước rằng câu trả lời này được nâng cấp nhiều hơn và tôi đã thấy nó sớm hơn, nó sẽ giúp tôi tiết kiệm thời gian ...
Yin Cognyto

5

Tôi đã so sánh một số phương pháp và nhận được kết quả với cách nhanh nhất để giải quyết vấn đề này. Đó là một forvòng lặp. Nó nhanh hơn 5 lần so với bất kỳ phương pháp nào khác.

Đây là trang thử nghiệm: https://jsbench.me/9hjewv6a98


Thiếu một for-ofvòng lặp không?
Douglas Gaskell

5

Trong khi, hầu hết các câu trả lời khác ở đây là hợp lệ. Đôi khi, tốt nhất là chỉ thực hiện một chức năng đơn giản ngắn gần nơi bạn sẽ sử dụng nó.

// indexOf wrapper for the list of objects
function indexOfbyKey(obj_list, key, value) {
    for (index in obj_list) {
        if (obj_list[index][key] === value) return index;
    }
    return -1;
}
// Find the string in the list (default -1)
var test1 = indexOfbyKey(object_list, 'name', 'Stevie');
var test2 = indexOfbyKey(object_list, 'last_name', 'some other name');

Nó phụ thuộc vào những gì quan trọng với bạn. Nó có thể tiết kiệm các dòng mã và rất thông minh khi sử dụng một lớp lót hoặc để đặt một giải pháp chung ở đâu đó bao gồm các trường hợp cạnh khác nhau. Nhưng đôi khi tốt hơn là chỉ nói: "ở đây tôi đã làm như thế này" thay vì để các nhà phát triển trong tương lai có thêm công việc kỹ thuật đảo ngược. Đặc biệt nếu bạn coi mình là "người mới" như trong câu hỏi của bạn.


4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

Tâm trí [0] .

Nó là thích hợp để sử dụng reducenhư trongAntonio Laguna câu trả lời.

Lời xin lỗi cho sự ngắn gọn ...


4

Nếu đối tượng của bạn là cùng một đối tượng của những đối tượng bạn đang sử dụng trong mảng, bạn sẽ có thể lấy chỉ mục của Đối tượng giống như cách bạn làm như thể đó là một chuỗi.

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var qazCLONE = { // new object instance and same structure
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [hello,qaz];

myArray.indexOf(qaz) // should return 1
myArray.indexOf(qazCLONE) // should return -1

Đây là câu trả lời tôi đang tìm kiếm vì tôi không rõ liệu IndexOf có khớp với giá trị hay không. Bây giờ tôi biết tôi có thể sử dụng IndexOf để tìm đối tượng của mình và không lo lắng nếu có các đối tượng khác có cùng thuộc tính.
MDave



3

Nếu bạn chỉ muốn tìm vị trí, hãy xem câu trả lời của @ Pablo .

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

Tuy nhiên, nếu bạn đang mong muốn tìm ra phần tử (nghĩa là nếu bạn đang nghĩ làm một cái gì đó như thế này myArray[pos]), có một cách hiệu quả hơn để thực hiện nó, sử dụng filter.

element = myArray.filter((e) => e.hello === 'stevie')[0];

Xem kết quả nước hoa (~ + 42% ops / giây): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3


2

Xem ví dụ này: http://jsfiddle.net/89C54/

for (i = 0; i < myArray.length; i++) {
    if (myArray[i].hello === 'stevie') {
        alert('position: ' + i);
        return;
    }
}

Nó bắt đầu đếm bằng không.


2

Tôi đã thực hiện một chức năng chung để kiểm tra bên dưới là mã & hoạt động cho bất kỳ đối tượng nào

function indexOfExt(list, item) {
    var len = list.length;

    for (var i = 0; i < len; i++) {
        var keys = Object.keys(list[i]);
        var flg = true;
        for (var j = 0; j < keys.length; j++) {
            var value = list[i][keys[j]];
            if (item[keys[j]] !== value) {
                flg = false;
            }
        }
        if (flg == true) {
            return i;
        }
    }
    return -1;
}

var items = [{ "hello": 'world', "foo": 'bar' }];
var selectedItem = { "hello": 'world', "foo": 'bar' };
alert(items.indexOf(selectedItem));
alert(indexOfExt(items, selectedItem));

Cảnh báo đầu tiên sẽ trả về -1 (có nghĩa là không tìm thấy kết quả khớp) & cảnh báo thứ hai sẽ trả về 0 (có nghĩa là tìm thấy kết quả khớp).


2

Sử dụng _.findIndextừ thư viện underscore.js

Đây là ví dụ _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1


Nếu đề xuất phương pháp từ các thư viện bổ sung, bạn nên đề cập đến nơi chúng đến.
Craicerjack

@Steve Bennett sử dụng tốt. Bây giờ thư viện đã ở trong lodash
zabusa

2

Sử dụng findIndexphương pháp ES6 , không có lodash hoặc bất kỳ thư viện nào khác, bạn có thể viết:

function deepIndexOf(arr, obj) {
  return arr.findIndex(function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

Điều này sẽ so sánh các thuộc tính ngay lập tức của đối tượng, nhưng không tái diễn vào các thuộc tính.

Nếu việc triển khai của bạn chưa cung cấp findIndex(hầu hết là không), bạn có thể thêm một polyfill nhẹ hỗ trợ tìm kiếm này:

function deepIndexOf(arr, obj) {
  function findIndex = Array.prototype.findIndex || function (pred) {
    for (let i = 0; i < this.length; ++i) {
      if (pred.call(this, this[i], i)) {
        return i;
      }
    }

    return -1;
  }

  return findIndex.call(arr, function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

(từ câu trả lời của tôi về bản dupe này )


2

Array.prototype.findIndex()Về cơ bản, bạn có thể sử dụng một chức năng tiện lợi và thuận tiện :

Phương thức find Index () trả về một chỉ mục trong mảng, nếu một phần tử trong mảng thỏa mãn chức năng kiểm tra được cung cấp. Nếu không -1 được trả về.

Chỉ cần lưu ý rằng nó không được hỗ trợ trên Internet Explorer, Opera và Safari, nhưng bạn có thể sử dụng Polyfill được cung cấp trong liên kết bên dưới.

Thêm thông tin:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find Index

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello, qaz);

var index = myArray.findIndex(function(element, index, array) {
  if (element.hello === 'stevie') {
    return true;
  }
});
alert('stevie is at index: ' + index);


2

Furthor của câu trả lời @Monika Garg , bạn có thể sử dụng findIndex()(Có một polyfill cho các trình duyệt không được hỗ trợ).

Tôi thấy rằng mọi người đã đánh giá thấp câu trả lời này và tôi hy vọng rằng họ đã làm điều này vì cú pháp sai, bởi vì theo tôi, đây là cách thanh lịch nhất.

Phương thức find Index () trả về một chỉ mục trong mảng, nếu một phần tử trong mảng thỏa mãn chức năng kiểm tra được cung cấp. Nếu không -1 được trả về.

Ví dụ:

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

var index = myArray.findIndex(function(element) {
  return element.hello == 'stevie';
});

alert(index);


Phương pháp có vẻ thanh lịch, nhưng không hỗ trợ IE theo MDN? developer.mozilla.org/en/docs/Web/JavaScript/Reference/,
Jaakko Karhu

Cố gắng sử dụng polyfill (liên kết trong câu trả lời).
Mosh Feu

1

Đây là cách để tìm chỉ mục của đối tượng trong mảng

    var myArray = [{  hello: 'world',
        foo: 'bar'
    },{
        hello: 'stevie',
        foo: 'baz'
    }];



    for (i = 0; i < myArray.length; i++) {
        if (myArray[i].hello === 'stevie') {
            alert('position: ' + i);
            return;
        }
    }

0

Điều này hoạt động mà không có mã tùy chỉnh

var arr, a, found;
arr = [{x: 1, y: 2}];
a = {x: 1, y: 2};
found = JSON.stringify(arr).indexOf(JSON.stringify(a)) > - 1;
// found === true

Lưu ý: điều này không đưa ra chỉ mục thực tế, nó chỉ cho biết nếu đối tượng của bạn tồn tại trong cấu trúc dữ liệu hiện tại


1
Điều này không hợp lệ vì điều này không cho phép nhận bất kỳ chỉ số nào
Antonio Laguna

0

Bạn chỉ có thể sử dụng

const someId = 2;
const array = [{id:1}, {id:2}, {id:3}];
const index = array.reduce((i, item, index) => item.id === someId ? index : i, -1);
alert('someId ' + someId + ' is at index ' + index);

Không gạch dưới, không cho, chỉ là giảm.


0
var hello = {hello: "world",  foo: "bar"};
var qaz = {hello: "stevie", foo: "baz"};
var myArray = [];
myArray.push(hello,qaz);

function indexOfObject( arr, key, value   ) {
    var j = -1;
    var result = arr.some(function(obj, i) { 
        j++;
        return obj[key] == value;
    })

    if (!result) {
        return -1;
    } else {
        return j;
    };
}

alert(indexOfObject(myArray,"hello","world"));

Sử dụng Array một số phương thức.
user3235365

-1

Bạn có thể tạo nguyên mẫu của riêng bạn để làm điều này:

cái gì đó như:

Array.prototype.indexOfObject = function (object) {
    for (var i = 0; i < this.length; i++) {
        if (JSON.stringify(this[i]) === JSON.stringify(object))
            return i;
    }
}

2
Thực hành không tốt, phá vỡ đóng gói: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/
Kẻ

Điều này cũng sẽ phá vỡ trên các đối tượng được xác định đệ quy.
Joseph Coco

-2

Tôi sẽ thích sử dụng findIndex()phương pháp:

 var index = myArray.findIndex('hello','stevie');

index sẽ cung cấp cho bạn số chỉ mục.


1
Trả lời, Chính tả và thụt mã và :) có sai không?
Sachin Verma

1
find Index không có trong bất kỳ triển khai tiêu chuẩn javascript nào. Có một đề xuất sắp tới (ecma 6) cho một phương pháp như vậy, nhưng chữ ký của nó không giống như vậy. Vui lòng làm rõ ý của bạn (có thể là tên của phương thức), đưa ra khai báo phương thức find Index hoặc đặt tên cho thư viện bạn đang sử dụng.
Sebastien F.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.