[JS] Làm thế nào kiểm tra object tồn tại trong array hay không?

Mình thấy rằng câu hỏi “Làm thế nào kiểm tra object tồn tại trong mảng hay không?” khi tìm kiếm bằng tiếng Việt trên Google thì câu trả lời nhận được dường như không rõ ràng và dễ gây nhầm lẫn. Đối với người đi làm rồi thì chuyện này không có gì quá khó khăn, chỉ cần đổi sang search bằng chút tiếng anh là ra được kết quả. Cơ mà với những bạn mới tập tành lập trình, cũng đang tập tành tiếng Anh thì hẳn có chút khó khăn (giống mình ngày xưa). Nên hôm nay mình ở đây để viết ra bài này.

Kiểm tra dựa tất cả thuộc tính

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var vegetables = [
{ id: 1, name: 'broccoli' },
{ id: 2, name: 'cabbage' },
{ id: 3, name: 'cauliflower' },
{ id: 4, name: 'eggplant' },
{ id: 5, name: 'sweet potato' },
{ id: 6, name: 'zucchini' },
];

function hasObjectInArray(targetObject, sourceArray) {
// chạy đến khi nào THỎA ĐIỀU KIỆN sẽ BREAK
return sourceArray.some(i => {
// kiểm tra số lượng properties bằng nhau không
// không bằng tức khác nhau
if (Object.keys(i).length !== Object.keys(targetObject).length) {
return false;
}

// TODO so sánh deep object

// hàm every chạy để kiểm tra TẤT CẢ properties của i bằng với peroperties của targetObject
// nhớ xài `===` để chắc chắn kiểu dữ liệu của các properties giống nhau
return Object.keys(i).every(k => i[k] === targetObject[k]);
});
}

var object1 = { id: 3, name: 'cauliflower' };
var object2 = { id: 3, name: 'zucchini' };

hasObjectInArray(object1, vegetables); // true
hasObjectInArray(object2, vegetables); // false

Chú ý các hàm some()every() là các method build sẵn trong ES6 để giúp duyệt mảng chất chơi người dơi hơn, về bản chất chúng đều là vòng lặp, nên các bạn có thể code bằng cách dùng for hoặc while gì đó nhá.

CHÚ Ý: bài toán sẽ phức tạp hơn nếu object kia lồng nhau, nghĩa là trong object lại có một object khác á. Này để bạn đọc tự khám phá nhé, simple nhất là dùng đệ quy.

Kiểm tra dựa vào 1 tiêu chí

Chú ý, việc so sánh tìm ra 2 object bằng nhau là khá khó khăn (so sánh tất cả các thuộc tính của object), nên phần dưới đây chúng ta sẽ đi tìm object trong mảng sẽ dựa vào một thuộc tính của object đó. Ví dụ student là một object, class là một mảng, muốn tìm một object (student) có trong mảng (class) hay không, thì dựa vào tên của học sinh (một thuộc tính), chứ tìm theo cách khác sẽ khá phức tạp.

Dùng some()

Đây là giải pháp khá nhanh gọn.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var vegetables = [
{ id: 1, name: 'broccoli' },
{ id: 2, name: 'cabbage' },
{ id: 3, name: 'cauliflower' },
{ id: 4, name: 'eggplant' },
{ id: 5, name: 'sweet potato' },
{ id: 6, name: 'zucchini' },
];

// ES6
vegetables.some(i => i.name === 'zucchini'); // true
vegetables.some(i => i.name === 'cucumber'); // false

// cách viết ES5
vegetables.some(function (item) {
return item.name === 'cauliflower';
}); // true

Dùng findIndex()

Đây là hàm mới nên có thể vài trình duyện cũ không hỗ trợ. Cơ mà như some vậy đó. Code gọn.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var partOfBody = [
{ id: 1, name: 'chest' },
{ id: 2, name: 'neck' },
{ id: 3, name: 'shoulder' },
{ id: 4, name: 'shin' },
{ id: 5, name: 'elbow' },
{ id: 6, name: 'knee' },
];

// ES6
partOfBody.findIndex(i => i.name === 'chin'); // -1
partOfBody.findIndex(i => i.name === 'knee'); // 5

// cách viết ES5
partOfBody.findIndex(function (item) {
return item.name === 'knee';
}); // 5

Hàm này trả về index nó tìm được trong mảng, nếu tìm không thấy sẽ trả về -1. Vậy chỉ cần check index trả về có >= 0 là được rồi.

Dùng filter()

Hàm filter mặc định sẽ trả về mảng mới, mảng mới này chứa data hay không phụ thuộc vào callback truyền vào filter trả về true/false.
i.name === 'chin' trả về true nghĩa là filter sẽ thêm i vào danh sách mảng sẽ trả về.

1
2
3
4
5
6
7
8
9
10
11
12
var partOfBody = [
{ id: 1, name: 'chest' },
{ id: 2, name: 'neck' },
{ id: 3, name: 'shoulder' },
{ id: 4, name: 'shin' },
{ id: 5, name: 'elbow' },
{ id: 6, name: 'knee' },
];

// ES6
var result = (partOfBody.filter(i => i.name === 'chin').length > 0); // false
var result2 = (partOfBody.filter(i => i.name === 'knee').length > 0); // true

Ngoài ra bạn có thể làm với các method khác của Array, như reduce chẳng hạn. Cơ chế đằng sau đều là chạy vòng lặp để kiểm tra. Trong phạm vi các giải pháp mình nêu ra, tối ưu nhất mà code lại đẹp, đó là dùng some() hoặc findIndex().

Bonus

Giải bài toán nếu object lồng object phía trên:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var somethings = [
{ id: 1, info: { name: 'broccoli', type: 'food' } },
{ id: 2, name: 'cabbage' },
{ id: 3, info: { name: 'cauliflower', type: 'food' } },
{ id: 4, name: 'eggplant' },
{ id: 5, name: 'sweet potato' },
{ id: 6, name: 'zucchini' },
];

function deepCompareObject(sourceObject, targetObject) {
return Object.keys(sourceObject).every(k => {
// TODO need check đầy đủ các kiểu của object, như Date ...
let bothIsObject = (sourceObject[k] && typeof sourceObject[k] === 'object' && targetObject[k] && typeof targetObject[k] === 'object');

if (bothIsObject) {
return deepCompareObject(sourceObject[k], targetObject[k])
}

// TODO check date, array

return sourceObject[k] === targetObject[k];
});
}

function hasObjectInArray(targetObject, sourceArray) {
return sourceArray.some(i => {
if (Object.keys(i).length !== Object.keys(targetObject).length) {
return false;
}

return deepCompareObject(i, targetObject);
});
}

var object1 = { id: 3, info: { name: 'cauliflower', type: 'food' } }; // true
var object2 = { id: 3, info: { name: 'chin', type: 'human-body' } }; // false
var object3 = { id: 1, info: { name: 'broccoli', type: 'human-body' } }; // false
var object4 = { id: 1, info: { name: 'broccoli', type: 'food' } }; // true

var result1 = hasObjectInArray(object1, somethings);
var result2 = hasObjectInArray(object2, somethings);
var result3 = hasObjectInArray(object3, somethings);
var result4 = hasObjectInArray(object4, somethings);

console.log(result1); // true
console.log(result2); // false
console.log(result3); // false
console.log(result4); // true

Còn những cái TODO trong code là bạn có thể viết thêm, tùy ý vào trường hợp của bạn. Còn trong phạm vi bài viết, mình chỉ ví dụ tới đấy thôi.

Vocabulary

  • broccoli 🥦: bông cải xanh, hay xào với thịt heo

  • cauliflower: bông cải trắng, cũng hay xào với thịt heo

  • cabbage 🥬: búp su

  • eggplant 🍆: cà tím

  • sweet potato 🍠: khoai lang

  • zucchini: bí ngòi, bí xanh, bí quả dài màu xanh, giống bí này bà con với bí đỏ, cơ mà nó màu xanh và quả dài

  • pumpkin 🎃: bí đỏ

  • cucumber 🥒: dưa leo, dưa chuột

  • chest: ngực

  • neck: cổ

  • shoulder: vai

  • shin: ống khuỷa chân

  • elbow: cùi chỏ

  • knee: đầu gối

Have fun. See you.

Tham khảo