Dalam larik objek, cara tercepat untuk menemukan indeks objek yang atributnya cocok dengan penelusuran

139

Saya telah mencari-cari sedikit mencoba menemukan cara yang efisien untuk melakukan ini, tetapi tidak berhasil. Saya memiliki array objek yang terlihat seperti ini:

array[i].id = some number;
array[i].name = some name;

Yang ingin saya lakukan adalah menemukan INDEKS dari objek di mana id sama dengan, misalnya, salah satu dari 0,1,2,3 atau 4. Saya kira saya bisa melakukan sesuatu seperti:

var indexes = [];
for(i=0; i<array.length; i++) {
  (array[i].id === 0) ? { indexes[0] = i }
  (array[i].id === 1) ? { indexes[1] = i }
  (array[i].id === 2) ? { indexes[2] = i }
  (array[i].id === 3) ? { indexes[3] = i }
  (array[i].id === 4) ? { indexes[4] = i }
}

Meskipun ini akan berhasil, tampaknya cukup mahal dan lambat (belum lagi jelek), terutama jika array.length bisa jadi besar. Adakah ide tentang bagaimana merapikan ini sedikit? Saya berpikir untuk menggunakan array.indexOf entah bagaimana tetapi saya tidak melihat bagaimana cara memaksakan sintaks. Ini

array.indexOf(this.id === 0);

misalnya, mengembalikan tidak terdefinisi, sebagaimana mestinya. Terima kasih sebelumnya!

Petrov
sumber
1
Jika Anda memiliki larik lama biasa, yang dapat Anda lakukan hanyalah mengulang. Itulah yang dimaksud dengan array, sekumpulan objek yang diurutkan berdasarkan indeks array.
Dave Newton
2
Cukup temukan posting ini hari ini, untuk semua yang terlambat ada metode array baru Array.prototype.findIndex()di ECMAScript 2015. Jawaban yang diterima sangat mengagumkan.
Conrad Lo
Saya penggemar sintaks ES6 (gunakan polyfill, jika diperlukan dukungan pada browser lama). ES7 + ES8 akan menjadi masa depan
Fr0zenFyr

Jawaban:

398

Mungkin Anda ingin menggunakan fungsi tingkat tinggi seperti "peta". Dengan asumsi Anda ingin mencari berdasarkan atribut 'field':

var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);
var objectFound = array[elementPos];
Pablo Francisco Pérez Hidalgo
sumber
9
Jawaban ini bagus karena sebenarnya menjawab pertanyaan dengan memberikan indeks :)
mengimbangi
3
@ZeroAbsolute Fungsi yang Anda terapkan (diteruskan ke peta) dapat mengembalikan string hash yang harus menyediakan kunci unik untuk setiap kemungkinan kombinasi yang diberikan oleh kriteria Anda. Sebagai contoh: function hashf(el) { return String(el.id) + "_" + String(el.name); }. Ini hanyalah petunjuk: elementPos = array.map(hashf(x)).indexOf(hash({id:3, name:'Pablo'}));Jelas, fungsi hash yang saya sediakan tidak valid untuk semua kasus karena '_'dapat menjadi bagian dari nilai Anda, tetapi ini hanya contoh singkat Anda dapat mengetahui metode hash yang berbeda.
Pablo Francisco Pérez Hidalgo
1
Apa ini kembali jika tidak ditemukan? Saya berasumsi -1, hanya penasaran. Saya akan bereksperimen.
Nathan C. Tresch
1
@ NathanC.Tresch Ia mengembalikan -1 karena itu adalah indexOfnilai kembali ketika tidak dapat menemukan nilai yang diberikan.
Pablo Francisco Pérez Hidalgo
2
Hai semuanya, alih-alih menggunakan dua metode, map, indexOfAnda dapat menggunakan hanya satu yang disebut findIndex....... Contoh:[{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3}) OR [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)
Umair Ahmed
69

Cara termudah dan termudah untuk menemukan indeks elemen dalam array.

Sintaks ES5: [{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3})

Sintaks ES6: [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)

Umair Ahmed
sumber
4
Saya yakin ini adalah solusi paling elegan. Bagi mereka yang khawatir tentang kompatibilitas mundur, Anda dapat menemukan polyfill findIndexdi developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mrogers
2
Saya mendapatkan peringatan di alat lint ES6 saya bahwa obj.id == 3operator yang digunakan di sini dapat menyebabkan konversi jenis yang tidak terduga, jadi gunakan obj.id === 3operator sebagai gantinya, yang menguji nilai dan jenis yang sama.
thclark
2
Jawaban ini setidaknya 3,5 kali lebih cepat dari jawaban yang diterima di atas. Menggunakannya var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);membutuhkan waktu 0,03500000002532033 milidetik. Menggunakannya [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)membutuhkan waktu 0,00999999747378752 milidetik.
Ovidio Reyna
1
JAWABAN INI adalah yang paling EFISIEN karena tidak mengulang seluruh larik. Jawaban yang dipilih akan memetakan array lengkap dan kemudian findIndex yang terikat untuk iterasi melalui seluruh array sekali
Karun
26

Metode Array baru .filter () akan bekerja dengan baik untuk ini:

var filteredArray = array.filter(function (element) { 
    return element.id === 0;
});

jQuery juga bisa melakukan ini dengan .grep ()

edit: perlu disebutkan bahwa kedua fungsi ini hanya beriterasi di bawah kap, tidak akan ada perbedaan kinerja yang nyata antara keduanya dan fungsi filter Anda sendiri bergulir, tetapi mengapa menciptakan kembali roda.

jbabey.dll
sumber
+1, Saya selalu lupa tentang fungsi bawaan seperti ini pada objek.
Tejs
59
Ini tidak mengembalikan indeks.
Adam Grant
Ini tidak menjawab pertanyaan khusus ini, tetapi banyak membantu saya! Terima kasih!
rochasdv
Ini tidak mengembalikan indeks.
Kaya
13

Jika Anda peduli dengan kinerja, jangan pergi dengan menemukan atau memfilter atau memetakan atau metode yang dibahas di atas

Berikut adalah contoh yang mendemonstrasikan metode tercepat. DI SINI adalah tautan ke tes yang sebenarnya

Blok penyiapan

var items = []

for(var i = 0; i < 1000; i++) {
    items.push({id: i + 1})
}

var find = 523

Metode Tercepat

var index = -1
for(var i = 0; i < items.length; i++) {
    if(items[i].id === find) {
        index = i;
        break;
    }
}

Metode yang Lebih Lambat

items.findIndex(item => item.id === find)

Metode TERLAMBAT

items.map(item => item.id).indexOf(find);
PirateApp
sumber
2
Terima kasih telah memberikan perbandingan ini! Yang sangat menarik adalah seberapa banyak kinerja bervariasi - termasuk metode mana yang lebih cepat bervariasi tergantung pada browser / mesin JavaScript yang digunakan untuk menjalankannya.
Iain Collins
1
Saya pikir ini harus ditandai sebagai jawaban. Ini menunjukkan cara tercepat dan yang lebih lambat.
Penghilang rasa sakit
Dalam patokan Anda, blok 2 (menggunakan findIndex) sebenarnya lebih cepat bagi saya (di Microsoft Edge Chromium 83.0.474.0)
rezadru
Block 2 sekarang juga lebih cepat di chrome
cody mikol
Saya telah memperpanjang benchmark jsben.ch/19PxA dan blok ke-4 ini adalah yang tercepat di Chrome. -> items.map (function (x) {return x.id;}). indexOf (temukan);
Günay Gültekin
8
array.forEach(function (elem, i) {  // iterate over all elements of array
    indexes[elem.id] = i;           // take the found id as index for the
});                                 // indexes array and assign i

hasilnya adalah daftar pencarian untuk id. dengan id yang diberikan kita mendapatkan indeks record.

Nina Scholz
sumber
6
var indices = [];
var IDs = [0, 1, 2, 3, 4];

for(var i = 0, len = array.length; i < len; i++) {
    for(var j = 0; j < IDs.length; j++) {
        if(array[i].id == ID) indices.push(i);
    }
}
Elliot Bonneville
sumber
6

Karena tidak ada jawaban yang menggunakan array biasa find:

var one = {id: 1, name: 'one'};
var two = {id: 2, name:'two'}
var arr = [one, two] 

var found = arr.find((a) => a.id === 2)

found === two // true

arr.indexOf(found) // 1
enapupe
sumber
3

Menggunakan mapfungsi ES6 :

let idToFind = 3;
let index = someArray.map(obj => obj.id).indexOf(idToFind);
JoeTidee
sumber
3

Cara baru menggunakan ES6

let picked_element = array.filter(element => element.id === 0);
Silve 2611
sumber
3

const index = array.findIndex(item => item.id === 'your-id');

Ini akan memberi Anda indeks item dalam array dengan id === your-id

array = [ {id:1}, {id:2} ];

const index = array.findIndex(item => item.id === 2);

console.log(index);

PulpDood
sumber
2

Kedengarannya seperti Anda bisa membuat iterator sederhana dengan callback untuk pengujian. Seperti:

function findElements(array, predicate)
{
    var matchingIndices = [];

    for(var j = 0; j < array.length; j++)
    {
        if(predicate(array[j]))
           matchingIndices.push(j);
    }

    return matchingIndices;
}

Kemudian Anda bisa memohon seperti ini:

var someArray = [
     { id: 1, text: "Hello" },
     { id: 2, text: "World" },
     { id: 3, text: "Sup" },
     { id: 4, text: "Dawg" }
  ];

var matchingIndices = findElements(someArray, function(item)
   {
        return item.id % 2 == 0;
   });

// Should have an array of [1, 3] as the indexes that matched
Tejs
sumber
2

Mengadaptasi jawaban Tejs untuk mongoDB dan Robomongo I berubah

matchingIndices.push(j);

untuk

matchingIndices.push(NumberInt(j+1));
pengguna2584621
sumber
2

Untuk meringkas semua jawaban bagus di atas dan tambahan jawaban saya tentang menemukan semua indeks terjadi dari beberapa komentar.

  1. Untuk mengembalikan indeks kejadian pertama.

const array = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 2 }];
const idYourAreLookingFor = 2;

//ES5 
//Output: 1
array.map(function (x) { return x.id; }).indexOf(idYourAreLookingFor);

//ES6 
//Output: 1
array.findIndex(obj => obj.id === idYourAreLookingFor);

  1. Untuk mengembalikan larik indeks dari semua kejadian, menggunakan reduce.

const array = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 2 }]
const idYourAreLookingFor = 2;

//ES5
//Output: [1, 4]
array.reduce(function (acc, obj, i) {
  if (obj.id === idYourAreLookingFor)
    acc.push(i);
  return acc;
}, []);

//ES6
//Output: [1, 4]
array.reduce((acc, obj, i) => (obj.id === idYourAreLookingFor) ? acc.concat(i) : acc, [])

trungk18
sumber
0

Karena saya belum bisa berkomentar, saya ingin menunjukkan solusi yang saya gunakan berdasarkan metode yang diposting Umair Ahmed, tetapi ketika Anda ingin mencari kunci alih-alih nilai:

[{"a":true}, {"f":true}, {"g":false}]
.findIndex(function(element){return Object.keys(element)[0] == "g"});

Saya mengerti bahwa ini tidak menjawab pertanyaan yang diperluas, tetapi judulnya tidak menentukan apa yang diinginkan dari setiap objek, jadi saya ingin dengan rendah hati membagikan ini untuk mengurangi sakit kepala kepada orang lain di masa mendatang, sementara saya membatalkannya mungkin bukan solusi tercepat.

Xander N
sumber
0

Saya telah membuat utilitas kecil yang disebut super-array di mana Anda dapat mengakses item dalam array dengan pengenal unik dengan kompleksitas O (1). Contoh:

const SuperArray = require('super-array');

const myArray = new SuperArray([
  {id: 'ab1', name: 'John'},
  {id: 'ab2', name: 'Peter'},
]);

console.log(myArray.get('ab1')); // {id: 'ab1', name: 'John'}
console.log(myArray.get('ab2')); // {id: 'ab2', name: 'Peter'}
patotoma
sumber
Anda mungkin ingin membaca Bagaimana cara menawarkan perpustakaan sumber terbuka pribadi? sebelum memposting ini di mana-mana.
Martijn Pieters
@MartijnPieters Saya telah mempostingnya hanya untuk beberapa pertanyaan yang relevan dan proyek ini gratis dari MIT jadi apa masalahnya? Mungkin Anda bisa sedikit lebih toleran.
patotoma
0
var test = [
  {id:1, test: 1},
  {id:2, test: 2},
  {id:2, test: 2}
];

var result = test.findIndex(findIndex, '2');

console.log(result);

function findIndex(object) {
  return object.id == this;
}

akan mengembalikan indeks 1 (Hanya berfungsi di ES 2016)

ekstrim
sumber
0

Saya menyukai metode ini karena mudah untuk membandingkan dengan nilai apa pun dalam objek tidak peduli seberapa dalam itu bersarang.

 while(i<myArray.length && myArray[i].data.value!==value){
  i++; 
}
// i now hows the index value for the match. 
 console.log("Index ->",i );
Daniel Lefebvre
sumber