ฉันจะตรวจสอบว่าอาร์เรย์มีค่าใน JavaScript ได้อย่างไร


3996

วิธีที่รัดกุมและมีประสิทธิภาพมากที่สุดในการค้นหาว่าอาร์เรย์ 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ซึ่งอยู่ในการหาวัตถุในอาร์เรย์ใช้


49
เพิ่งทดสอบ: วิธีของคุณเป็นวิธีที่เร็วที่สุดสำหรับเบราว์เซอร์ต่าง ๆ : jsperf.com/find-element-in-obj-vs-array/2 (นอกเหนือจากการบันทึก a.length ในตัวแปรล่วงหน้า) ในขณะที่ใช้ indexOf (เช่นเดียวกับ $ .inArray) ช้ากว่ามาก
Jörn Berkefeld

17
หลายคนตอบว่า Array # indexOf เป็นตัวเลือกที่ดีที่สุดของคุณที่นี่ แต่ถ้าคุณต้องการบางสิ่งที่สามารถส่งไปยังบูลีนได้อย่างถูกต้องให้ใช้สิ่งนี้: ~[1,2,3].indexOf(4)จะคืนค่า 0 ซึ่งจะประเมินว่าเป็นเท็จในขณะที่~[1,2,3].indexOf(3)จะคืนค่า -3 ซึ่งจะประเมินว่าเป็นจริง
lordvlad

8
~!ไม่ใช่สิ่งที่คุณต้องการใช้ในการแปลงแบบบูลเพื่อที่คุณต้องการ แต่ในกรณีนี้คุณต้องการตรวจสอบความเท่าเทียมกันกับ -1 ดังนั้นฟังก์ชันอาจจบลงreturn [1,2,3].indexOf(3) === -1; ~ด้วยเลขฐานสองไม่ใช่มันจะกลับค่าแต่ละบิต
mcfedr

14
@Iordvlad [1,2,3].indexOf(4)จริงจะกลับ -1 @mcfedr ชี้ให้เห็นว่า~เป็นผู้ประกอบการระดับบิต - ไม่ดู ES5 11.4.8 สิ่งคือเนื่องจากการแสดงเลขฐานสองของ-1ประกอบด้วย 1 เท่านั้นมันเป็นส่วนประกอบ0ซึ่งประเมินว่าเป็นเท็จ การเติมเต็มของหมายเลขอื่นใดจะไม่ใช่ศูนย์ดังนั้นจริง ดังนั้นทำงานได้ดีและมักจะใช้ร่วมกับ~ indexOf
mknecht

5
ชื่อเรื่องนั้นทำให้เข้าใจผิด ในกรณีที่เป็น[[1,2],[3,4]].includes([3,4])?
mplungjan

คำตอบ:


4375

เบราว์เซอร์ที่ทันสมัยมีArray#includesซึ่งไม่ตรงกับที่และได้รับการสนับสนุนอย่างกว้างขวางโดยทุกคนยกเว้น IE:

console.log(['joe', 'jane', 'mary'].includes('jane')); //true

คุณยังสามารถใช้Array#indexOfซึ่งน้อยกว่าโดยตรง แต่ไม่ต้องการโพลีฟิลสำหรับเบราว์เซอร์ที่ล้าสมัย


เฟรมเวิร์กจำนวนมากเสนอวิธีการที่คล้ายกัน:

ขอให้สังเกตว่าบางเฟรมเวิร์คใช้สิ่งนี้เป็นฟังก์ชั่นในขณะที่คนอื่น ๆ เพิ่มฟังก์ชั่นให้กับต้นแบบอาร์เรย์


42
MooTools ยังมี Array.containers ที่ส่งคืนบูลีนซึ่งฟังดูเหมือนคำถามจริงที่นี่
Ryan Florence

22
ต้นแบบยังมีการArray.includeคืนค่าบูลีนด้วย
user102008

46
หากคุณใช้เบราว์เซอร์ที่ดีคุณสามารถใช้งานได้array.indexOf(object) != -1
Sam Soffes

13
นอกจากนี้อย่าใช้ indexOf เพียงอย่างเดียวเป็นเงื่อนไขเนื่องจากองค์ประกอบแรกจะส่งกลับค่า 0 และจะถูกประเมินว่าเป็นเท็จ
บวก -

241
inArrayเป็นชื่อที่แย่มากสำหรับฟังก์ชั่นที่คืนค่าดัชนีขององค์ประกอบและ-1หากไม่มีอยู่ ฉันคาดหวังว่าจะได้บูลีนที่จะส่งคืน
ทิม

434

อัปเดตจาก 2019: คำตอบนี้มาจาก 2008 (อายุ 11 ปี!) และไม่เกี่ยวข้องกับการใช้งาน JS ที่ทันสมัย การปรับปรุงประสิทธิภาพที่ได้สัญญาไว้นั้นเป็นไปตามเกณฑ์มาตรฐานที่ทำในเบราว์เซอร์ในขณะนั้น อาจไม่เกี่ยวข้องกับบริบทการดำเนินการ JS สมัยใหม่ หากคุณต้องการทางออกที่ง่ายให้มองหาคำตอบอื่น ๆ หากคุณต้องการประสิทธิภาพที่ดีที่สุดให้สร้างมาตรฐานสำหรับตัวคุณเองในสภาพแวดล้อมการดำเนินการที่เกี่ยวข้อง

ดังที่คนอื่น ๆ กล่าวไว้การวนซ้ำผ่านอาร์เรย์อาจเป็นวิธีที่ดีที่สุด แต่ได้รับการพิสูจน์แล้วว่าการwhileวนซ้ำที่ลดลงเป็นวิธีที่เร็วที่สุดในการวนซ้ำใน JavaScript ดังนั้นคุณอาจต้องการเขียนรหัสของคุณใหม่ดังนี้:

function contains(a, obj) {
    var i = a.length;
    while (i--) {
       if (a[i] === obj) {
           return true;
       }
    }
    return false;
}

แน่นอนคุณอาจขยายต้นแบบ Array ได้เช่นกัน:

Array.prototype.contains = function(obj) {
    var i = this.length;
    while (i--) {
        if (this[i] === obj) {
            return true;
        }
    }
    return false;
}

และตอนนี้คุณสามารถใช้สิ่งต่อไปนี้:

alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false


22
"พิสูจน์แล้ว" เป็นคำที่หนักแน่น เอ็นจิ้นของ JS ปรับปรุงอย่างต่อเนื่องและเวลาดำเนินการที่วัดได้เมื่อ 3 ปีก่อนนั้นล้าสมัยไปอย่างมาก
orip

2
@Damir - ฉันเห็นด้วย อาจเปลี่ยนตัวอย่างเพื่อใช้ indexOf หากมีให้ใช้ดังนั้นผู้คนที่คัดลอกโค้ดนี้สุ่มสี่สุ่มห้าจะได้รับประสิทธิภาพที่ดีที่สุดเท่าที่จะทำได้
orip

1
@cbmeeks ใช่จำเป็นต้องมีการดูแลอย่างแน่นอน มันอาจจะเป็นกรณีของการทำfor (o in array)ที่ไม่ควรทำเมื่อวนลูปผ่านอาร์เรย์โดยทั่วไป ...
Damir Zekić

1
วิธีที่ดีที่สุดในการทำเช่นนี้คือตรวจสอบว่า [1, 2, 3] .indexOf (1)> -1
Devin G Rhode

207

indexOf อาจเป็นได้ แต่เป็น "ส่วนขยาย JavaScript ของมาตรฐาน ECMA-262 ดังนั้นจึงอาจไม่มีอยู่ในการปรับใช้มาตรฐานอื่น ๆ "

ตัวอย่าง:

[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1

AFAICS Microsoft ไม่เสนอทางเลือกประเภทนี้ แต่คุณสามารถเพิ่มฟังก์ชันการทำงานที่คล้ายกันในอาร์เรย์ใน Internet Explorer (และเบราว์เซอร์อื่นที่ไม่สนับสนุนindexOf) หากคุณต้องการเนื่องจากการค้นหาโดย Google ที่รวดเร็ว (ตัวอย่างเช่นอันนี้ )


จริงๆแล้วมีตัวอย่างของการใช้งานส่วนขยาย indexOf สำหรับเบราว์เซอร์ที่ไม่สนับสนุนในหน้า developer.mozilla.org ที่คุณเชื่อมโยง
Lloyd Cotten

ที่จริงแล้วถ้าคุณเพิ่ม indexof ไปยังต้นแบบของ Array สำหรับเบราว์เซอร์ที่ไม่รองรับ (เช่น IE7) พวกเขาจะพยายามวนฟังก์ชั่นนี้เมื่อวนรอบรายการในอาร์เรย์ น่ารังเกียจ
CpILL


มันใช้สำหรับตรวจสอบวัตถุหรือไม่ ฉันไม่คิดว่ามันจะใช้งานได้ในกรณีของวัตถุ
Himesh Aadeshara

169

ECMAScript 7 Array.prototype.includesเปิดตัว

มันสามารถใช้เช่นนี้:

[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

ซึ่งแตกต่างจากindexOfที่ใช้เข้มงวดเปรียบเทียบความเท่าเทียมกัน , includesเปรียบเทียบโดยใช้SameValueZeroอัลกอริทึมเท่าเทียมกัน นั่นหมายความว่าคุณสามารถตรวจสอบได้ว่าอาร์เรย์มีNaN:

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

ไม่เหมือนindexOfกันincludesอย่าข้ามดัชนีที่ขาดหายไป:

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

ขณะนี้ยังคงเป็นร่าง แต่สามารถpolyfilledเพื่อให้ทำงานบนเบราว์เซอร์ทั้งหมด


3
ไม่รองรับ IE และ Microsfot Edge (2015) ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...... )
Adriano Resende

1
นอกจากนี้ยังเกี่ยวข้องกับตารางความเข้ากันได้ ES7 (ดูเหมือนว่าโครเมี่ยมรองรับตอนนี้)
รูปแบบ

มันใช้สำหรับตรวจสอบวัตถุหรือไม่ ฉันไม่คิดว่ามันจะทำงานได้ในกรณีของวัตถุ
Himesh Aadeshara

128

คำตอบอันดับแรกถือว่าเป็นประเภทดั้งเดิม แต่ถ้าคุณต้องการทราบว่าอาร์เรย์มีวัตถุที่มีลักษณะบางอย่างหรือไม่Array.prototype.some ()เป็นคำตอบที่สวยงามมาก:

const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]

items.some(item => item.a === '3')  // returns true
items.some(item => item.a === '4')  // returns false

สิ่งที่ดีเกี่ยวกับเรื่องนี้ก็คือการทำซ้ำจะถูกยกเลิกเมื่อองค์ประกอบพบว่าวงจรการทำซ้ำที่ไม่จำเป็นจะได้รับการงดเว้น

นอกจากนี้ยังเหมาะอย่างยิ่งในifคำสั่งเพราะมันกลับบูลีน:

if (items.some(item => item.a === '3')) {
  // do something
}

* ตามที่ jamess ชี้ให้เห็นในความคิดเห็นในเวลาที่คำตอบนี้เดือนกันยายน 2018 Array.prototype.some()ได้รับการสนับสนุนอย่างเต็มที่: ตารางการสนับสนุน caniuse.com


1
ตั้งแต่วันนี้, กันยายน 2018, Array.prototype.some () ได้รับการสนับสนุนอย่างเต็มรูปแบบ: ตารางสนับสนุน
caniuse.com

1
ทำงานในโหนด> = 8.10 สำหรับแลมบ์ดา AWS Node.js ดังนั้นมันจึงยอดเยี่ยม ทางออกที่สะอาดและเรียบง่ายมาก! 👍🏻
จอร์แดน

1
@jamess อาจได้รับการสนับสนุนเป็นอย่างดี แต่โปรดจำไว้ว่าArrow functionsในตัวอย่างนี้ไม่ได้รับการสนับสนุนที่ดี สำหรับรายละเอียดเพิ่มเติมดูที่นี่: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Kamil Witkowski

มีไฟฟ้าลัดวงจรบ้างไหม? หรือทำซ้ำอาร์เรย์ทั้งหมดแม้ว่าจะพบค่าหรือไม่
Douglas Gaskell

@DouglasGaskell มันยกเลิกซ้ำพบว่าเมื่อ (กล่าวถึงในคำตอบ)
ไมเคิล

112

สมมติว่าคุณได้กำหนดอาร์เรย์ไว้ดังนี้:

const array = [1, 2, 3, 4]

ด้านล่างมีสามวิธีในการตรวจสอบว่ามี3ในนั้นหรือไม่ ทั้งหมดของพวกเขากลับมาอย่างใดอย่างหนึ่งหรือtruefalse

วิธี Native Array (ตั้งแต่ ES2016) ( ตารางความเข้ากันได้ )

array.includes(3) // true

เป็นวิธี Array แบบกำหนดเอง (ก่อน ES2016)

// Prefixing the method with '_' to avoid name clashes
Object.defineProperty(Array.prototype, '_includes', { value: function (v) { return this.indexOf(v) !== -1 }})
array._includes(3) // true

ฟังก์ชั่นที่เรียบง่าย

const includes = (a, v) => a.indexOf(v) !== -1
includes(array, 3) // true

มันจะกลับมาจริงถ้า "b" อยู่ในอาเรย์ "a" ... ฉันไม่รู้ว่าจะอธิบายยังไงดี ...
william malo

4
ตอนนี้ฉันไม่เข้าใจ "!! ~" และฉันคิดว่านี่จะไม่ทำงานใน IE8 เพราะ IE8 ไม่รองรับ indexOf () บนวัตถุ Array
svlada

62
"~" เป็นโอเปอเรเตอร์ที่แบ่งชั้นกลับด้านและลบ 1 จากตัวเลข indexOf ส่งกลับ -1 ถ้ามันล้มเหลวดังนั้น "~" จะเปลี่ยน -1 เป็น "0" ใช้ "!!" เปลี่ยนตัวเลขให้เป็น boleans (!! 0 === false)
william malo

1
เจ๋ง แต่จริงจังเพื่อความเรียบง่ายไม่ใช่แค่ a.indexOf (b)> - 1 ตั้งแต่ "> -1" .length === "!! ~" .length
super

2
ฉันจะเรียกการขาดความรู้เกี่ยวกับผลกระทบของผู้ประกอบการบูลีนไม่เป็นมืออาชีพ แต่ฉันเห็นด้วยเกี่ยวกับคุณค่าของรหัสที่อ่านได้ฉันจะห่อมันด้วยฟังก์ชั่นที่มีป้ายกำกับชัดเจน และนั่นคือสิ่งที่เฟรมเวิร์ก JS หลักส่วนใหญ่ทำ
okdewit

79

นี่คือการใช้งานร่วมกันได้ของJavaScript 1.6Array.indexOf :

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

สิ่งนี้ดูดี แต่สับสนเล็กน้อย: * การทดสอบในบรรทัดที่ 1 และ 3 ไม่เทียบเท่าหรือไม่ * จะดีกว่าไหมถ้าจะทดสอบต้นแบบและเพิ่มฟังก์ชันใน Array.prototype ถ้าจำเป็น?
Avi Flax

10
พวกเขาไม่เท่าเทียมกัน เป็นชวเลข[].indexOf Array.prototype.indexOfโปรแกรมเมอร์จาวาสคริปต์ที่ป้องกันเราหวาดระแวงหลีกเลี่ยงการขยายต้นแบบดั้งเดิมที่ค่าใช้จ่ายทั้งหมด
MárÖrlygsson

1
ไม่ได้[].indexOfสร้างอาร์เรย์ใหม่แล้วเข้าถึงindexOfในขณะที่Array.prototype.indexOfเข้าถึงต้นแบบโดยตรงหรือไม่
alex

3
alex @ ใช่[].indexOf === Array.prototype.indexOf(ลองมันออกมาใน FireBug) [].indexOf !== Array.indexOfแต่ตรงกันข้าม
MárÖrlygsson

57

ใช้:

function isInArray(array, search)
{
    return array.indexOf(search) >= 0;
}

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}

25
x ? true : falseมักจะซ้ำซ้อน มันอยู่ที่นี่
Ry-

@minitech ทำไมคุณพูดว่าซ้ำซ้อน?
MatíasCánepa

8
array.indexOf(search) >= 0เป็นบูลีนอยู่แล้ว return array.indexOf(search) >= 0เพียงแค่
Ry-

@ minitech ดีขอบคุณ! จริงๆแล้วฉันไม่รู้ว่าจะสามารถคืนสิ่งก่อสร้างได้ TIL มีอะไรใหม่
MatíasCánepa

แท้จริงการสร้างใด ๆ ในจาวาสคริปต์สามารถคืน
BT

49

การขยายArrayวัตถุJavaScript เป็นความคิดที่แย่มากเพราะคุณแนะนำคุณสมบัติใหม่ (วิธีการที่คุณกำหนดเอง) เป็นfor-inวงที่สามารถแบ่งสคริปต์ที่มีอยู่ ไม่กี่ปีที่ผ่านมาผู้เขียนห้องสมุดต้นแบบต้องปรับโครงสร้างการใช้งานห้องสมุดใหม่เพื่อลบสิ่งเหล่านี้ออกไป

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


22
ฉันไม่เห็นด้วย. For-in loops ไม่ควรใช้อาร์เรย์เพราะเหตุผลนี้ การใช้ for-in loops จะแตกเมื่อใช้หนึ่งใน js ไลบรารียอดนิยม
Tomas

นี่จะถือว่าเป็นการปะของลิงหรือไม่ ฮ่า ๆ บางคนชอบที่
cbmeeks

33

หนึ่งในสายการบิน:

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

8
array.filter(e=>e==x).length > 0เทียบเท่าarray.some(e=>e==x)แต่someมีประสิทธิภาพมากขึ้น
Apolo

32

คิดนอกกรอบสักครู่ถ้าคุณโทรมาหลายครั้งมันจะมีประสิทธิภาพมากกว่าในการใช้อาเรย์แบบเชื่อมโยง Map เพื่อทำการค้นหาโดยใช้ฟังก์ชันแฮช

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map


แม้ว่าสิ่งนี้จะเป็นประโยชน์กับคนจำนวนมาก แต่มันจะดีกว่าถ้ามีการเพิ่มข้อมูลโค้ด
พาย 'Oh' Pah

28

ฉันใช้สิ่งต่อไปนี้:

Array.prototype.contains = function (v) {
    return this.indexOf(v) > -1;
}

var a = [ 'foo', 'bar' ];

a.contains('foo'); // true
a.contains('fox'); // false

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

Array.prototype.some ()ถูกเพิ่มเข้าในมาตรฐาน ECMA-262 ในรุ่นที่ 5


ถ้าใช้ es6 มากกว่าแคมจะสั้นลงเป็นcontains = (a, obj) => a.some((element) => element === obj))
diEcho

แม้ IE9 มีการสนับสนุนArray.prototype.some ()ECMAScript 5
Suncat2000

19

หวังว่าแบบสองทิศทางindexOf/ lastIndexOfทางเลือกที่เร็วขึ้น

2015

ในขณะที่วิธีการใหม่รวมถึงดีมากการสนับสนุนเป็นศูนย์สำหรับตอนนี้

เป็นเวลานานที่ฉันคิดถึงวิธีที่จะแทนที่ฟังก์ชัน indexOf / lastIndexOf ที่ช้า

พบวิธีที่นักแสดงแล้วดูคำตอบยอดนิยม จากที่ฉันเลือกcontainsฟังก์ชั่นที่โพสต์โดย @Damir Zekic ซึ่งน่าจะเร็วที่สุด แต่มันก็ยังระบุว่ามาตรฐานมาจากปี 2008 และล้าสมัย

ฉันชอบwhileมากกว่าforแต่ไม่ใช่เหตุผลเฉพาะที่ฉันสิ้นสุดการเขียนฟังก์ชันด้วย for for loop 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 (โดยพื้นฐานแล้วtrueด้วยindexและfalseด้วย-1) ไม่ควรทำอันตราย

ชุดต้นแบบอาร์เรย์

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');

ฟังก์ชั่นนี้ยังสามารถแก้ไขได้อย่างง่ายดายเพื่อคืนค่าจริงหรือเท็จหรือแม้กระทั่งวัตถุสตริงหรืออะไรก็ตาม

และนี่คือ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


18

ประสิทธิภาพ

วันนี้ 2020.01.07 ฉันทำการทดสอบบน MacOs HighSierra 10.13.6 บน Chrome v78.0.0, Safari v13.0.4 และ Firefox v71.0.0 สำหรับ 15 โซลูชั่นที่เลือก สรุปผลการวิจัย

  • ขึ้นอยู่กับการแก้ปัญหาJSON, Setและที่น่าแปลกใจfind(K, N, O) เป็นที่ช้าที่สุดบนเบราว์เซอร์
  • es6 includes(F) เร็วเพียงกับโครเมียมเท่านั้น
  • โซลูชันที่ใช้for(C, D) และindexOf(G, H) นั้นค่อนข้างเร็วสำหรับเบราว์เซอร์ทั้งหมดในอาร์เรย์ขนาดเล็กและขนาดใหญ่ดังนั้นอาจเป็นตัวเลือกที่ดีที่สุดสำหรับโซลูชันที่มีประสิทธิภาพ
  • การแก้ปัญหาที่ดัชนีลดลงในช่วงห่วง (B) จะช้าอาจจะเป็นเพราะทางของCPU งานแคช
  • ฉันยังรันการทดสอบอาร์เรย์ขนาดใหญ่เมื่อองค์ประกอบการค้นหาอยู่ในตำแหน่ง 66% ของความยาวอาร์เรย์และโซลูชันที่อิงตามfor(C, D, E) ให้ผลลัพธ์ที่คล้ายกัน (~ 630 ops / วินาที - แต่ E บนซาฟารีและ Firefox คือ 10- ช้ากว่า C และ D 20%

ผล

ป้อนคำอธิบายรูปภาพที่นี่

รายละเอียด

ฉันทำการทดสอบ 2 กรณี: สำหรับอาเรย์ที่มี 10 องค์ประกอบและอาเรย์ที่มี 1 milion องค์ประกอบ ในทั้งสองกรณีเราใส่อิลิเมนต์การค้นหาในอาร์เรย์กลาง

Array ขนาดเล็ก - 10 องค์ประกอบ

คุณสามารถทำการทดสอบในเครื่องของคุณที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

Array ใหญ่ - 1,000,000 องค์ประกอบ

คุณสามารถทำการทดสอบในเครื่องของคุณที่นี่

ป้อนคำอธิบายรูปภาพที่นี่


16

หากคุณกำลังใช้ JavaScript 1.6 หรือสูงกว่า (Firefox 1.5 หรือใหม่กว่า) คุณสามารถใช้Array.indexOf มิฉะนั้นฉันคิดว่าคุณกำลังจะจบลงด้วยสิ่งที่คล้ายกับรหัสต้นฉบับของคุณ


16
function inArray(elem,array)
{
    var len = array.length;
    for(var i = 0 ; i < len;i++)
    {
        if(array[i] == elem){return i;}
    }
    return -1;
} 

ส่งคืนดัชนีอาร์เรย์ถ้าพบหรือ -1 ถ้าไม่พบ


16

เราใช้ตัวอย่างนี้ (ทำงานกับวัตถุ, อาร์เรย์, สตริง):

/*
 * @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

15

หากคุณกำลังตรวจสอบซ้ำ ๆ ว่ามีวัตถุอยู่ในอาเรย์คุณควรตรวจสอบ

  1. รักษาอาร์เรย์ที่เรียงตลอดเวลาโดยทำการเรียงลำดับการแทรกในอาร์เรย์ของคุณ (ใส่วัตถุใหม่ในสถานที่ที่เหมาะสม)
  2. ทำให้การอัปเดตวัตถุเป็นลบ + ดำเนินการแทรกเรียงและ
  3. ใช้ค้นหา binarycontains(a, obj)ค้นหาในของคุณ

2
หรือถ้าเป็นไปได้ให้หยุดใช้ Array โดยสิ้นเชิงและใช้ Object เป็นพจนานุกรมแทนตามที่ MattMcKnight และ ninjagecko แนะนำ
joeytwiddle

13

โซลูชันที่ใช้งานได้ในเบราว์เซอร์สมัยใหม่ทั้งหมด:

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.indexOfและArray.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-keys


อัปเดตcontainsฟังก์ชั่นด้วยการเพิ่มประสิทธิภาพที่สมบูรณ์แบบ ขอบคุณมากสำหรับการชี้ให้เห็น


รหัสก้อนนี้อาจทำงานใน IE6 (ยังไม่ได้ทดสอบ) แต่ IE ไม่รองรับ ES5 จนกระทั่ง IE9
Mark Reed

สำหรับเหตุผลด้านประสิทธิภาพคุณควรหลีกเลี่ยงการกำหนดสตริง อย่างน้อยคุณควรหลีกเลี่ยง JSON.stringify "obj" ในทุก ๆ วงเพราะมันมีราคาแพงและจะทำให้ใบสมัครของคุณช้าลง ดังนั้นคุณควรจับภาพหน้า for-loop ในตัวแปร temp
itinance

1
@ จุดแข็งที่ดี อัปเดตincludesฟังก์ชั่นพร้อมคำแนะนำของคุณ ฉันวิ่ง jsperf ด้วยฟังก์ชั่นของฉัน ช้ากว่าของ lodash ประมาณ 5 เท่า แม้ว่า lodash ไม่ได้เปรียบเทียบโดยค่าและไม่สามารถหาใน{a: 1} [{a: 1}]ฉันไม่รู้ว่ามีห้องสมุดใดไหม แต่ฉันอยากรู้ว่ามีนักแสดงอีกหรือไม่และไม่ซับซ้อนอย่างที่ทำ
Igor Barbashin

ล่าช้า: สิ่งนี้ใช้ไม่ได้กับพูดcontains([{ a: 1, b: 2 }], { b: 2, a: 1 })เพราะวัตถุที่เป็นสตริงรักษาลำดับของคุณสมบัติไว้
นอกรีต Monkey

1
@HereticMonkey จริง นั่นเป็นเหตุผลที่ฉันเพิ่มsort-keysบันทึกที่ด้านล่าง
Igor Barbashin

12

ใช้ lodash เป็นบางฟังก์ชั่น

มันกระชับถูกต้องและมีการสนับสนุนข้ามแพลตฟอร์มที่ยอดเยี่ยม

คำตอบที่ยอมรับนั้นไม่เป็นไปตามข้อกำหนด

ข้อกำหนด:แนะนำวิธีรัดกุมและมีประสิทธิภาพมากที่สุดเพื่อค้นหาว่าอาร์เรย์ 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)

10

ECMAScript 6 มีข้อเสนอสุดหรูในการค้นหา

เมธอด find เรียกใช้งานฟังก์ชัน callback หนึ่งครั้งสำหรับแต่ละองค์ประกอบที่มีอยู่ในอาร์เรย์จนกว่าจะพบวิธีที่ callback คืนค่าจริง หากพบองค์ประกอบดังกล่าวให้ค้นหาส่งกลับค่าขององค์ประกอบนั้นทันที มิฉะนั้นค้นหาส่งคืนไม่ได้กำหนด การเรียกกลับถูกเรียกใช้เฉพาะสำหรับดัชนีของอาร์เรย์ที่มีค่าที่กำหนดไว้เท่านั้น มันไม่ได้ถูกเรียกใช้สำหรับดัชนีที่ถูกลบไปหรือที่ไม่เคยมีการกำหนดค่า

นี่คือเอกสาร MDNที่

ฟังก์ชันการค้นหาใช้งานได้เช่นนี้

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

นี่เป็นมาตรฐาน: ecma-international.org/ecma-262/6.0/#sec-array.prototype.find
Madbreaks

9

แม้ว่าarray.indexOf(x)!=-1จะเป็นวิธีที่รัดกุมที่สุดในการทำเช่นนี้ (และได้รับการสนับสนุนโดยเบราว์เซอร์ที่ไม่ใช่ Internet Explorer มานานกว่าทศวรรษ ... ) แต่ก็ไม่ใช่ O (1) แต่เป็น O (N) ซึ่งแย่มาก หากอาร์เรย์ของคุณจะไม่เปลี่ยนแปลงคุณสามารถแปลงอาร์เรย์ของคุณเป็น hashtable จากนั้นทำ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.contain เพื่อ "หยุด" อาร์เรย์และเก็บ hashtable ใน this._cache ในสองบรรทัดนี้จะให้ผลลัพธ์ที่ผิดถ้าคุณเลือกที่จะแก้ไขอาร์เรย์ของคุณในภายหลัง JavaScript มี hooks ไม่เพียงพอที่จะ ให้คุณรักษาสถานะนี้ไม่เหมือนกับ Python)


9

หนึ่งสามารถใช้ชุดที่มีวิธีการ "มี ()":

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'));


5
ฉันคิดว่าreturn proxy.has(obj)สะอาดกว่าสองบรรทัดด้วยคำสั่ง if-else ที่นี่
Maciej Bukowski

function contains(arr, obj) { return new Set(arr).has(obj); }
Gordon Bean

8

ใช้:

var myArray = ['yellow', 'orange', 'red'] ;

alert(!!~myArray.indexOf('red')); //true

การสาธิต

หากต้องการทราบว่าสิ่งที่tilde ~ต้องทำ ณ จุดนี้ให้อ้างอิงคำถามนี้ตัวหนอนจะทำอะไรเมื่อมันนำหน้านิพจน์ .


5
นี่ถูกโพสต์แล้วเมื่อครึ่งปีที่แล้วไม่จำเป็นต้องทำซ้ำ
Shadow Wizard คือ Ear For You

3
จริงๆแล้วมันยังไม่ได้โพสต์ ไม่ได้เป็นคำตอบ แต่เป็นความเห็นต่อคำตอบและถึงแม้จะไม่ชัดเจนและรัดกุม ขอบคุณสำหรับการโพสต์มัน Mina Gabriel
T.CK

6

ตกลงคุณสามารถเพิ่มประสิทธิภาพรหัสของคุณเพื่อรับผล!

มีหลายวิธีในการทำสิ่งนี้ที่สะอาดกว่าและดีกว่า แต่ฉันแค่อยากได้รูปแบบของคุณและนำไปใช้กับสิ่งนั้นโดยใช้JSON.stringifyเพียงแค่ทำสิ่งนี้ในกรณีของคุณ:

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

ล่าช้า: สิ่งนี้ใช้ไม่ได้กับพูดcontains([{ a: 1, b: 2 }], { b: 2, a: 1 })เพราะวัตถุที่เป็นสตริงรักษาลำดับของคุณสมบัติไว้
นอกรีตลิง

5

ไม่ได้ดีที่สุด แต่ฉันเพิ่งได้รับความคิดสร้างสรรค์และเพิ่มไปยังเพลง

ห้ามใช้สิ่งนี้

Object.defineProperty(Array.prototype, 'exists', {
  value: function(element, index) {

    var index = index || 0

    return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index)
  }
})


// Outputs 1
console.log(['one', 'two'].exists('two'));

// Outputs -1
console.log(['one', 'two'].exists('three'));

console.log(['one', 'two', 'three', 'four'].exists('four'));


สิ่งที่คุณควรใช้ถ้าไม่ใช่สิ่งนี้
bryc

@bryc อาจเป็นทางออกที่ยอมรับหรือวิธีแก้ไขปัญหาอื่นจากที่นี่ หากคุณไม่สนใจเรื่องการแสดงมากนักคุณสามารถใช้สิ่งนี้ได้
sqram

5

แปลกใจที่คำถามนี้ยังไม่มีการเพิ่มไวยากรณ์ล่าสุดเพิ่ม 2 เซนต์ของฉัน

สมมติว่าเรามีอาเรย์ของ Objects arrObj และเราต้องการค้นหา obj ในนั้น

Array.prototype indexOf -> (return index หรือ -1 ) โดยทั่วไปจะใช้สำหรับการค้นหาดัชนีขององค์ประกอบในอาร์เรย์ นอกจากนี้ยังสามารถใช้สำหรับการค้นหาวัตถุ แต่ใช้ได้เฉพาะเมื่อคุณส่งการอ้างอิงไปยังวัตถุเดียวกัน

let obj = { name: 'Sumer', age: 36 };
let arrObj = [obj, { name: 'Kishor', age: 46 }, { name: 'Rupen', age: 26 }];


console.log(arrObj.indexOf(obj));// 0
console.log(arrObj.indexOf({ name: 'Sumer', age: 36 })); //-1

console.log([1, 3, 5, 2].indexOf(2)); //3

Array.prototype รวม -> (ส่งคืนจริงหรือเท็จ )

console.log(arrObj.includes(obj));  //true
console.log(arrObj.includes({ name: 'Sumer', age: 36 })); //false

console.log([1, 3, 5, 2].includes(2)); //true

Array.prototype find -> (รับการเรียกกลับส่งคืนค่าแรก/ วัตถุที่ส่งกลับค่าจริงใน CB)

console.log(arrObj.find(e => e.age > 40));  //{ name: 'Kishor', age: 46 }
console.log(arrObj.find(e => e.age > 40)); //{ name: 'Kishor', age: 46 }

console.log([1, 3, 5, 2].find(e => e > 2)); //3

Array.prototype findIndex -> (รับการเรียกกลับส่งคืนดัชนีของค่า / วัตถุแรกที่ส่งคืนจริงใน CB)

console.log(arrObj.findIndex(e => e.age > 40));  //1
console.log(arrObj.findIndex(e => e.age > 40)); //1

console.log([1, 3, 5, 2].findIndex(e => e > 2)); //1

เนื่องจาก find และ findIndex รับการเรียกกลับเราสามารถดึงวัตถุใด ๆ (แม้ว่าเราจะไม่มีการอ้างอิง) จากอาร์เรย์โดยการตั้งค่าเงื่อนไขที่สร้างสรรค์อย่างสร้างสรรค์


5

วิธีแก้ปัญหาอย่างง่ายสำหรับข้อกำหนดนี้คือการใช้ find()

หากคุณมีวัตถุหลายอย่างดังต่อไปนี้

var users = [{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "admin"},
{id: "105", name: "user"}];

จากนั้นคุณสามารถตรวจสอบว่าวัตถุที่มีค่าของคุณมีอยู่แล้วหรือไม่

let data = users.find(object => object['id'] === '104');

หากข้อมูลเป็นโมฆะจะไม่มีผู้ดูแลระบบมิฉะนั้นจะส่งคืนออบเจ็กต์ที่มีอยู่ด้านล่าง

{id: "104", name: "admin"}

จากนั้นคุณสามารถค้นหาดัชนีของวัตถุนั้นในอาร์เรย์และแทนที่วัตถุโดยใช้รหัสด้านล่าง

let indexToUpdate = users.indexOf(data);
let newObject = {id: "104", name: "customer"};
users[indexToUpdate] = newObject;//your new object
console.log(users);

คุณจะได้รับค่าเช่นด้านล่าง

[{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "customer"},
{id: "105", name: "user"}];

หวังว่าสิ่งนี้จะช่วยให้ทุกคน


5

    function countArray(originalArray) {
     
    	var compressed = [];
    	// make a copy of the input array
    	var copyArray = originalArray.slice(0);
     
    	// first loop goes over every element
    	for (var i = 0; i < originalArray.length; i++) {
     
    		var count = 0;	
    		// loop over every element in the copy and see if it's the same
    		for (var w = 0; w < copyArray.length; w++) {
    			if (originalArray[i] == copyArray[w]) {
    				// increase amount of times duplicate is found
    				count++;
    				// sets item to undefined
    				delete copyArray[w];
    			}
    		}
     
    		if (count > 0) {
    			var a = new Object();
    			a.value = originalArray[i];
    			a.count = count;
    			compressed.push(a);
    		}
    	}
     
    	return compressed;
    };
    
    // It should go something like this:
    
    var testArray = new Array("dog", "dog", "cat", "buffalo", "wolf", "cat", "tiger", "cat");
    var newArray = countArray(testArray);
    console.log(newArray);

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.