Làm thế nào để có được chỉ mục của đối tượng bởi thuộc tính của nó trong JavaScript?


239

Ví dụ: tôi có:

var Data = [
  { id_list: 1, name: 'Nick', token: '312312' },
  { id_list: 2, name: 'John', token: '123123' },
]

Sau đó, tôi muốn sắp xếp / đảo ngược đối tượng này bằng name, ví dụ. Và sau đó tôi muốn có được một cái gì đó như thế này:

var Data = [
  { id_list: 2, name: 'John', token: '123123' },
  { id_list: 1, name: 'Nick', token: '312312' },
]

Và bây giờ tôi muốn biết chỉ mục của đối tượng với thuộc tính name='John'để lấy giá trị của mã thông báo thuộc tính.

Làm thế nào để tôi giải quyết vấn đề?


Tại sao bạn muốn sắp xếp danh sách trước khi tìm kiếm tài sản?
JJJ

Đối tượng JavaScript là {Key:Value}, tôi đã sửa nó cho bạn.
Tên lửa Hazmat


Nếu bạn quét qua các câu trả lời, có vẻ như có một số Datađối tượng bản địa . Nó chỉ đơn thuần là một tên biến được viết hoa, trái với quy ước. Nếu bất cứ ai khác bị làm phiền bởi điều này, tôi sẽ chỉnh sửa câu hỏi và câu trả lời để sửa lỗi đặt tên này.
Roy bắt đầu

Câu trả lời:


170

Như các câu trả lời khác cho thấy, lặp qua mảng có lẽ là cách tốt nhất. Nhưng tôi sẽ đặt nó trong chức năng của chính nó, và làm cho nó trừu tượng hơn một chút:

function findWithAttr(array, attr, value) {
    for(var i = 0; i < array.length; i += 1) {
        if(array[i][attr] === value) {
            return i;
        }
    }
    return -1;
}

var Data = [
    {id_list: 2, name: 'John', token: '123123'},
    {id_list: 1, name: 'Nick', token: '312312'}
];

Với điều này, không chỉ bạn có thể tìm thấy cái nào chứa 'John' mà bạn có thể tìm thấy cái nào chứa mã thông báo '312312':

findWithAttr(Data, 'name', 'John'); // returns 0
findWithAttr(Data, 'token', '312312'); // returns 1
findWithAttr(Data, 'id_list', '10'); // returns -1

EDIT: Hàm được cập nhật để trả về -1 khi không tìm thấy để nó tuân theo cấu trúc tương tự như Array.prototype.indexOf ()


1
Đã thêm câu trả lời mở rộng điều này để bao gồm tìm kiếm sâu hơn một cấp thuộc tính. Cảm ơn vì điều này Chris - thực sự đã giúp: D
Ed Shee

3
Đối với những người sẽ gặp vấn đề với giải pháp này, hãy nhớ rằng dấu bằng === sẽ không chỉ kiểm tra giá trị mà còn cả kiểu dữ liệu. vì vậy, so sánh ngày, số và giá trị trống có thể trả về false nếu giá trị thực là một chuỗi. Bạn có thể sử dụng == dấu bằng nếu kiểu dữ liệu không có bản chất đối với bạn.
David Addoteye

ĐỪNG LÀM ĐIỀU NÀY. Tìm hiểu làm thế nào để sử dụng các hàm bậc cao hơn, đây là cách làm cũ.
Dẫn

325

Vì phần sắp xếp đã được trả lời. Tôi sẽ đề xuất một cách thanh lịch khác để lấy indexOf của một thuộc tính trong mảng của bạn

Ví dụ của bạn là:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},
    {id_list:2,name:'John',token:'123123'}
]

Bạn có thể làm:

var index = Data.map(function(e) { return e.name; }).indexOf('Nick');

Array.prototype.mapkhông có sẵn trên IE7 hoặc IE8. Khả năng tương thích ES5

Và đây là cú pháp ES6 và mũi tên, thậm chí còn đơn giản hơn:

const index = Data.map(e => e.name).indexOf('Nick');

4
Tôi phải lặp lại toàn bộ mảng không có vấn đề gì. Phương pháp đầu tiên không cần.
Đức Attanasio

1
Đơn giản, nhưng hãy tính đến hiệu suất khi sử dụng bộ dữ liệu lớn
gavioto

1
Câu trả lời thực sự tuyệt vời đã giúp tôi với vấn đề này!
NiallMitch14

1
Giải pháp này thật đáng yêu. Mong các bạn sống đến một ngàn tuổi, thưa ngài.
đào sâu

3
Thực hiện bản đồ và sau đó indexOf trong phiên bản ES6 có nghĩa là họ sẽ lặp lại mảng hai lần. Thay vào đó, bạn nên sử dụng phương thức find Index được hiển thị trong câu trả lời của tôi
silverlight513

208

Nếu bạn ổn với việc sử dụng ES6. Mảng hiện có chức năng find Index . Điều đó có nghĩa là bạn có thể làm một cái gì đó như thế này:

const index = Data.findIndex(item => item.name === 'John');

6
đưa ra giải pháp trực tiếp và thanh lịch nhất, tôi sẽ làm cho một điều nhỏ không phải là find Index là tham lam - trong trường hợp của tôi là hoàn hảo - nhưng người dùng lưu ý rằng nếu bạn có các đối tượng có giá trị trùng lặp (nghĩa là hai đối tượng name: John) thì điều này sẽ chỉ trả về trận đấu đầu tiên
GrayedFox

Tuyệt vời!! Cảm ơn bạn.
moreirapontocom

@GrayedFox không có nghĩa là bạn không tham lam? Tham lam có nghĩa là nhiều kết quả hơn và find Index chỉ trả về kết quả khớp đầu tiên.
Jos

1
Xin lỗi @Jos nhưng tôi nghĩ bạn không hiểu thuật toán tìm kiếm tham lam làm gì . Một thuật toán tham lam chỉ xem xét thông tin mà nó có trong tay, có hiệu quả về mặt tính toán để tìm kiếm một mục trong danh sách khi bạn chỉ cần một kết quả. Phương thức find Index là tham lam trước vì nó chỉ trả về kết quả khớp đầu tiên. Nếu không tham lam, nó sẽ tiếp tục tìm kiếm. Bạn đang nghĩ về thuật ngữ "tham lam" khi nó được sử dụng trong tiếng Anh hàng ngày, nhưng trong bối cảnh lập trình (ví dụ SO), nó trái ngược với những gì bạn nghĩ;)
GrayedFox

Cảm ơn đã giải thích @GrayedFox. Tài liệu tham khảo của tôi về sự tham lam thực ra cũng là từ lập trình nhưng liên quan đến các biểu thức thông thường trong đó sự tham lam chiếm nhiều nhân vật hơn là không tham lam! :-)
Jos

28
var index = Data.findIndex(item => item.name == "John")

Đây là phiên bản đơn giản hóa của:

var index = Data.findIndex(function(item){ return item.name == "John"})

Từ mozilla.org:

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


2
Sử dụng "===" để so sánh chuỗi, đó là điều duy nhất còn thiếu trong câu trả lời này.
Bruno Tavares

@BrunoTavares, bạn nghĩ kịch bản nào sẽ khác trong trường hợp này?
edank

1
@edank Nếu OP kiểm tra tokentrường thay vì nametrường có thể có vấn đề, như Data[0].token == 312312đánh giá true, nhưng Data[0].token === 321321đánh giá false.
kem

@edank hãy xem câu trả lời này stackoverflow.com/questions/359494/
Bruno Tavares

23

Nếu bạn gặp sự cố với IE, bạn có thể sử dụng hàm map () được hỗ trợ từ 9.0 trở đi:

var index = Data.map(item => item.name).indexOf("Nick");

1
Câu trả lời dưới đây cung cấp thêm thông tin và được đăng đầu tiên.
Alexander

Đây chắc chắn là giải pháp tốt nhất cho việc này.
Bebolicy

4

Cách duy nhất được biết với tôi là lặp qua tất cả các mảng:

var index=-1;
for(var i=0;i<Data.length;i++)
  if(Data[i].name==="John"){index=i;break;}

hoặc trường hợp không nhạy cảm:

var index=-1;
for(var i=0;i<Data.length;i++)
  if(Data[i].name.toLowerCase()==="john"){index=i;break;}

Trên chỉ mục biến kết quả chứa chỉ mục của đối tượng hoặc -1 nếu không tìm thấy.


2

một cách nguyên mẫu

(function(){
  if (!Array.prototype.indexOfPropertyValue){
    Array.prototype.indexOfPropertyValue = function(prop,value){
      for (var index = 0; index < this.length; index++){
        if (this[index][prop]){
          if (this[index][prop] == value){
            return index;
          }
        }
      }
      return -1;
    }
  }
 })();
 // usage:
 var Data = [
 {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}];
 Data.indexOfPropertyValue('name','John'); // returns 1 (index of array);
 Data.indexOfPropertyValue('name','Invalid name') // returns -1 (no result);
 var indexOfArray = Data.indexOfPropertyValue('name','John');
 Data[indexOfArray] // returns desired object.

Tôi đã phải thay đổi điều này một chút để làm việc cho tình huống của mình - Tôi đang tìm kiếm chỉ mục của một tài sản có giá trị null và dòng thứ năm đã khiến điều này bỏ qua chỉ số đó.
David Brunow

1

Bạn có thể sử dụng Array.sort bằng cách sử dụng một hàm tùy chỉnh làm tham số để xác định mecanism sắp xếp của bạn.

Trong ví dụ của bạn, nó sẽ cung cấp:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
]

Data.sort(function(a, b){
    return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});

alert("First name is : " + Data[0].name); // alerts 'John'
alert("Second name is : " + Data[1].name); // alerts 'Nick'

Hàm sắp xếp phải trả về -1 nếu a nên đến trước b, 1 nếu a nên đến sau b và 0 nếu cả hai đều bằng nhau. Tùy thuộc vào bạn để xác định logic đúng trong chức năng sắp xếp của bạn để sắp xếp mảng.

Chỉnh sửa: Bỏ lỡ phần cuối của câu hỏi của bạn, nơi bạn muốn biết chỉ mục. Bạn sẽ phải lặp qua các mảng để tìm thấy điều đó như những người khác đã nói.


1

Chỉ cần đi qua mảng của bạn và tìm vị trí:

var i = 0;
for(var item in Data) {
    if(Data[item].name == 'John')
        break;
    i++;
}
alert(i);

Điều này nên if(Data[item].name == 'John'), tôi đã sửa nó cho bạn.
Tên lửa Hazmat

Điều này có một khoảnh khắc. Nếu 'không tìm thấy' biến i chứa chỉ số dương. Và để kiểm tra 'không tìm thấy' cần so sánh nếu i === Data.length
Andrew D.

Khoảnh khắc thứ hai là nếu mảng chứa các thuộc tính tùy chỉnh nonindexer. Ví dụ: Dữ liệu var [1,2,3,4,5]; Dữ liệu ["kiểm tra"] = 7897; So sánh đầu ra của cảnh báo : for (var i in Data) (Dữ liệu [i]); cho cảnh báo (var i = 0; i <Data.length; i ++) (Dữ liệu [i]);
Andrew D.


1

Tại sao bạn không sử dụng một công việc nhỏ?

Tạo một mảng mới với tên là chỉ mục. sau đó tất cả các tìm kiếm sẽ sử dụng các chỉ mục. Vì vậy, chỉ có một vòng lặp. Sau đó, bạn không cần phải lặp qua tất cả các yếu tố!

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
    ]
var searchArr = []
Data.forEach(function(one){
  searchArr[one.name]=one;
})
console.log(searchArr['Nick'])

http://jsbin.com/xibala/1/edit

ví dụ sống.


Điều đó có nghĩa là lặp đi lặp lại mọi thứ trước tiên ... sau đó sửa đổi các đối tượng (trừ khi bạn sao chép chúng nhưng điều đó thậm chí còn nhiều chi phí hơn). Và sau đó bạn lặp lại (ít nhất một phần).
Jos

1

Câu trả lời mở rộng của Chris Pickett vì trong trường hợp của tôi, tôi cần tìm kiếm sâu hơn một cấp thuộc tính:

function findWithAttr(array, attr, value) {
  if (attr.indexOf('.') >= 0) {
    var split = attr.split('.');
    var attr1 = split[0];
    var attr2 = split[1];
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr1][attr2] === value) {
        return i;
      }
    }
  } else {
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr] === value) {
        return i;
      }
    }
  };
};

Bạn có thể truyền 'attr1.attr2' vào hàm


1

Cái này thì sao ? :

Data.indexOf(_.find(Data, function(element) {
  return element.name === 'John';
}));

Giả sử bạn đang sử dụng lodash hoặc underscorejs.


1
var fields = {
teste:
{
    Acess:
    {
        Edit: true,
      View: false

      }
 },
teste1:
{
    Acess:
    {
        Edit: false,
      View: false

      }
  }
};

console.log(find(fields,'teste'));
function find(fields,field){
    for(key in fields){
        if(key == field){
        return true;
    }
}
return false;
}

Nếu bạn có một Đối tượng có nhiều đối tượng bên trong, nếu bạn muốn biết liệu đối tượng nào đó có được bao gồm trên đối tượng Master hay không, chỉ cần đặt find (MasterObject, 'Object to Search'), hàm này sẽ trả về phản hồi nếu tồn tại hay không (TRUE hoặc FALSE ), Tôi hy vọng giúp đỡ với điều này, có thể thấy ví dụ trên JSFiddle .


Thêm một số giải thích với câu trả lời về cách câu trả lời này giúp OP khắc phục vấn đề hiện tại
ρяσѕρєя K

@ ρяσѕρєяK, Cảm ơn lời đề nghị của bạn, tôi đã thêm lời giải thích :)
Pedro Monteiro Arantes

1
let indexOf = -1;
let theProperty = "value"
let searchFor = "something";

theArray.every(function (element, index) {

    if (element[theProperty] === searchFor) {
        indexOf = index;
        return false;
    }
    return true;
});

1
collection.findIndex(item => item.value === 'smth') !== -1

Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn. Xin vui lòng cố gắng không làm đông mã của bạn với các bình luận giải thích, vì điều này làm giảm khả năng đọc của cả mã và các giải thích!
Tạm biệt StackExchange

1

Nếu bạn muốn nhận giá trị của mã thông báo thuộc tính thì bạn cũng có thể thử điều này

    let data=[
      { id_list: 1, name: 'Nick', token: '312312' },
      { id_list: 2, name: 'John', token: '123123' },
    ]

    let resultingToken =  data[_.findKey(data,['name','John'])].token

trong đó _.findKey là hàm của lodash


0

Ngoài ra, với câu trả lời của Attanasio Ruiz của Đức, bạn có thể loại bỏ vòng lặp thứ 2 bằng cách sử dụng Array.reduce () thay vì Array.map ();

var Data = [
    { name: 'hypno7oad' }
]
var indexOfTarget = Data.reduce(function (indexOfTarget, element, currentIndex) {
    return (element.name === 'hypno7oad') ? currentIndex : indexOfTarget;
}, -1);

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.