ในอาร์เรย์ของวัตถุวิธีที่เร็วที่สุดในการค้นหาดัชนีของวัตถุที่มีคุณลักษณะตรงกับการค้นหา


140

ฉันท่องไปรอบ ๆ เพื่อพยายามหาวิธีที่มีประสิทธิภาพในการทำสิ่งนี้ แต่ไม่มีที่ไหนเลย ฉันมีอาร์เรย์ของวัตถุที่มีลักษณะเช่นนี้:

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

สิ่งที่ฉันต้องการทำคือค้นหา INDEXES ของวัตถุที่ id เท่ากับตัวอย่างหนึ่งใน 0,1,2,3 หรือ 4 ฉันคิดว่าฉันสามารถทำสิ่งที่ชอบ:

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 }
}

แม้ว่าจะใช้งานได้ แต่ก็ดูเหมือนจะค่อนข้างแพงและช้า (ไม่ต้องพูดถึงน่าเกลียด) โดยเฉพาะอย่างยิ่งถ้า array.length อาจมีขนาดใหญ่ ความคิดใด ๆ เกี่ยวกับวิธีทำให้มันดูดีขึ้นหน่อย ฉันคิดว่าจะใช้ array.indexOf แต่ฉันไม่เห็นวิธีบังคับไวยากรณ์ นี้

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

ตัวอย่างเช่นส่งคืนไม่ได้กำหนดตามที่ควรจะเป็น ขอบคุณล่วงหน้า!


1
หากคุณมีอาร์เรย์เก่าธรรมดาสิ่งที่คุณทำได้คือทำซ้ำ นั่นคือสิ่งที่อาร์เรย์คือกลุ่มของวัตถุที่เรียงลำดับโดยดัชนีอาร์เรย์
Dave Newton

2
เพิ่งมาเจอโพสต์นี้วันนี้สำหรับผู้ที่มาสายทุกคนมีวิธีอาร์เรย์ใหม่Array.prototype.findIndex()ใน ECMAScript 2015 คำตอบที่ได้รับการยอมรับนั้นยอดเยี่ยมมาก
Conrad Lo

ฉันเป็นแฟนของไวยากรณ์ ES6 (ใช้ polyfills หากจำเป็นต้องรองรับเบราว์เซอร์เดิม) ES7 + ES8 กำลังจะเป็นอนาคต
Fr0zenFyr

คำตอบ:


398

บางทีคุณอาจต้องการใช้ฟังก์ชันลำดับที่สูงขึ้นเช่น "แผนที่" สมมติว่าคุณต้องการค้นหาตามแอตทริบิวต์ "ฟิลด์":

var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);
var objectFound = array[elementPos];

9
คำตอบนี้ดีมากเพราะจริง ๆ แล้วมันตอบคำถามโดยการให้ดัชนี :)
counterbeing

3
@ZeroAbsolute ฟังก์ชันที่ใช้ของคุณ (ส่งผ่านไปยังแผนที่) สามารถส่งคืนสตริงแฮชซึ่งควรให้คีย์เฉพาะสำหรับชุดค่าผสมที่เป็นไปได้แต่ละชุดตามเกณฑ์ของคุณ ตัวอย่างเช่น: function hashf(el) { return String(el.id) + "_" + String(el.name); }. นี่เป็นเพียงคำใบ้เท่านั้นelementPos = array.map(hashf(x)).indexOf(hash({id:3, name:'Pablo'}));เห็นได้ชัดว่าฟังก์ชันแฮชที่ฉันให้นั้นไม่สามารถใช้ได้กับทุกกรณีเนื่องจาก'_'อาจเป็นส่วนหนึ่งของค่าของคุณ แต่เป็นเพียงตัวอย่างสั้น ๆ ที่คุณสามารถหาวิธีการแฮชที่แตกต่างกันได้
Pablo Francisco Pérez Hidalgo

1
สิ่งนี้จะส่งคืนอะไรหากไม่พบ? ฉันถือว่า -1 แค่อยากรู้ ฉันจะทดลอง
Nathan C. Tresch

1
@ NathanC.Tresch ส่งกลับค่า -1 เนื่องจากเป็นindexOfค่าส่งคืนเมื่อไม่สามารถหาค่าที่กำหนด
Pablo Francisco Pérez Hidalgo

2
สวัสดีทุกคนแทนที่จะใช้สองวิธีmap, indexOfคุณสามารถใช้เพียงวิธีเดียวที่เรียกว่าfindIndex....... เช่น:[{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

วิธีที่ง่ายและสะดวกที่สุดในการค้นหาดัชนีองค์ประกอบในอาร์เรย์

ไวยากรณ์ ES5: [{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3})

ไวยากรณ์ ES6: [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)


4
ฉันเชื่อว่านี่เป็นทางออกที่สง่างามที่สุด สำหรับผู้ที่กังวลเกี่ยวกับความเข้ากันได้แบบย้อนกลับคุณสามารถค้นหา polyfill ได้findIndexที่developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mrogers

2
ฉันได้รับคำเตือนในเครื่องมือผ้าสำลี ES6 ของฉันว่าตัวobj.id == 3ดำเนินการที่ใช้ที่นี่อาจทำให้เกิดการแปลงประเภทที่ไม่คาดคิดได้ดังนั้นให้ใช้ตัวobj.id === 3ดำเนินการแทนซึ่งจะทดสอบค่าและประเภทที่เท่ากัน
thclark

2
คำตอบนี้เร็วกว่าคำตอบข้างต้นอย่างน้อย 3.5 เท่า การใช้var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);มันใช้เวลา 0.03500000002532033 มิลลิวินาทีโดยใช้[{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)เวลา 0.00999999747378752 มิลลิวินาที
Ovidio Reyna

1
คำตอบนี้มีประสิทธิภาพมากที่สุดเนื่องจากไม่ได้ทำซ้ำอาร์เรย์ทั้งหมด คำตอบที่เลือกจะแมปอาร์เรย์ที่สมบูรณ์จากนั้น findIndex ซึ่งถูกผูกไว้เพื่อวนซ้ำผ่านอาร์เรย์ทั้งหมดหนึ่งครั้ง
การุ ณ

26

เมธอด Array ใหม่. filter ()จะทำงานได้ดีสำหรับสิ่งนี้:

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

jQuery สามารถทำได้ด้วย. grep ()

แก้ไข: เป็นเรื่องที่ควรค่าแก่การกล่าวถึงว่าฟังก์ชั่นทั้งสองนี้เพียงวนซ้ำภายใต้ประทุนจะไม่มีความแตกต่างด้านประสิทธิภาพที่เห็นได้ชัดเจนระหว่างฟังก์ชันเหล่านี้และการหมุนฟังก์ชันฟิลเตอร์ของคุณเอง แต่ทำไมต้องประดิษฐ์วงล้อขึ้นมาใหม่


+1 ฉันมักจะลืมเกี่ยวกับฟังก์ชันในตัวเช่นนี้ในวัตถุ
Tejs

59
สิ่งนี้ไม่ส่งคืนดัชนี
Adam Grant

สิ่งนี้ไม่ได้ตอบคำถามเฉพาะนี้ แต่ช่วยฉันได้มาก! ขอบคุณ!
rochasdv

สิ่งนี้ไม่ส่งคืนดัชนี
รวย

14

หากคุณสนใจเกี่ยวกับประสิทธิภาพอย่าใช้การค้นหาหรือตัวกรองหรือแผนที่หรือวิธีการใด ๆ ที่กล่าวถึงข้างต้น

นี่คือตัวอย่างที่สาธิตวิธีการที่เร็วที่สุด นี่คือลิงค์ไปยังการทดสอบจริง

บล็อกการตั้งค่า

var items = []

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

var find = 523

วิธีที่เร็วที่สุด

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

วิธีการที่ช้าลง

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

วิธีที่ช้าที่สุด

items.map(item => item.id).indexOf(find);

2
ขอบคุณที่ให้การเปรียบเทียบนี้! สิ่งที่น่าสนใจเป็นอย่างยิ่งคือประสิทธิภาพแตกต่างกันไป - รวมถึงวิธีการใดที่เร็วขึ้นแตกต่างกันไปขึ้นอยู่กับเบราว์เซอร์ / เอ็นจิ้น JavaScript ที่ใช้ในการเรียกใช้
Iain Collins

1
ฉันคิดว่านี่ควรถูกทำเครื่องหมายเป็นคำตอบ นี่แสดงวิธีที่เร็วที่สุดและวิธีที่ช้ากว่า
Painkiller

ในเกณฑ์มาตรฐานของคุณบล็อก 2 (โดยใช้ findIndex) เร็วกว่าสำหรับฉัน (บน Microsoft Edge Chromium 83.0.474.0)
rezadru

ตอนนี้บล็อก 2 เร็วขึ้นใน chrome แล้ว
cody mikol

ฉันได้ขยายมาตรฐานjsben.ch/19PxAและบล็อกที่ 4 นี้เร็วที่สุดใน Chrome -> items.map (ฟังก์ชัน (x) {return x.id;}). indexOf (find);
GünayGü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

ผลลัพธ์คือรายการค้นหาสำหรับรหัส ด้วยรหัสที่กำหนดเราจะได้รับดัชนีของบันทึก



6

เนื่องจากไม่มีคำตอบโดยใช้อาร์เรย์ปกติ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



3

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

สิ่งนี้จะทำให้คุณได้รับดัชนีของรายการในอาร์เรย์ด้วย id === your-id

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

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

console.log(index);


2

สำหรับฉันดูเหมือนว่าคุณสามารถสร้างตัววนซ้ำแบบง่ายๆพร้อมการโทรกลับเพื่อทดสอบ ชอบมาก:

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

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

    return matchingIndices;
}

จากนั้นคุณสามารถเรียกได้ดังนี้:

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


2

เพื่อสรุปคำตอบที่ยอดเยี่ยมทั้งหมดข้างต้นและคำตอบเพิ่มเติมของฉันเกี่ยวกับการค้นหาดัชนีทั้งหมดที่เกิดขึ้นจากความคิดเห็นบางส่วน

  1. เพื่อส่งกลับดัชนีของเหตุการณ์แรก

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. ในการส่งคืนอาร์เรย์ดัชนีของเหตุการณ์ทั้งหมดโดยใช้การลด

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, [])


0

เนื่องจากฉันยังไม่สามารถแสดงความคิดเห็นได้ฉันต้องการแสดงวิธีแก้ปัญหาที่ฉันใช้ตามวิธีที่ Umair Ahmed โพสต์ แต่เมื่อคุณต้องการค้นหาคีย์แทนที่จะเป็นค่า:

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

ฉันเข้าใจว่ามันไม่ได้ตอบคำถามที่ขยายออก แต่ชื่อไม่ได้ระบุสิ่งที่ต้องการจากแต่ละวัตถุดังนั้นฉันจึงอยากแบ่งปันสิ่งนี้อย่างถ่อมตนเพื่อช่วยให้คนอื่น ๆ ปวดหัวในอนาคตในขณะที่ฉันยังไม่เริ่มต้นมันอาจไม่ใช่ ทางออกที่เร็วที่สุด


0

ฉันได้สร้างยูทิลิตี้เล็ก ๆ ที่เรียกว่าsuper-arrayซึ่งคุณสามารถเข้าถึงรายการในอาร์เรย์โดยใช้ตัวระบุเฉพาะที่มีความซับซ้อน O (1) ตัวอย่าง:

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'}

คุณอาจต้องการอ่านHow to offer personal open-source libraries? ก่อนโพสต์สิ่งนี้ทุกที่
Martijn Pieters

@MartijnPieters ฉันโพสต์ไว้เฉพาะคำถามที่เกี่ยวข้องสองสามข้อและโครงการนี้เป็นของ MIT ฟรีข้อตกลงคืออะไร? บางทีคุณอาจจะอดทนอีกนิด
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;
}

จะส่งคืนดัชนี 1 (ใช้ได้เฉพาะใน ES 2016)


0

ฉันชอบวิธีนี้เพราะง่ายต่อการเปรียบเทียบกับค่าใด ๆ ในวัตถุไม่ว่าจะซ้อนกันลึกแค่ไหนก็ตาม

 while(i<myArray.length && myArray[i].data.value!==value){
  i++; 
}
// i now hows the index value for the match. 
 console.log("Index ->",i );
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.