Đối tượng so với mảng trong Javascript cho cặp khóa / giá trị


83

Giả sử bạn có cấu trúc dữ liệu rất đơn giản:

(personId, name)

... và bạn muốn lưu trữ một số trong số này trong một biến javascript. Như tôi thấy, bạn có ba lựa chọn:

// a single object
var people = {
    1 : 'Joe',
    3 : 'Sam',
    8 : 'Eve'
};

// or, an array of objects
var people = [
    { id: 1, name: 'Joe'},
    { id: 3, name: 'Sam'},
    { id: 8, name: 'Eve'}
];

// or, a combination of the two
var people = {
    1 : { id: 1, name: 'Joe'},
    3 : { id: 3, name: 'Sam'},
    8 : { id: 8, name: 'Eve'}
};

Tùy chọn thứ hai hoặc thứ ba rõ ràng là cách tốt nhất nếu bạn có (hoặc mong đợi rằng bạn có thể có) nhiều hơn một phần "giá trị" để lưu trữ (ví dụ: thêm tuổi của chúng hoặc thứ gì đó), vì vậy, để tranh luận, giả sử rằng sẽ không bao giờ có thêm bất kỳ giá trị dữ liệu nào cần thiết trong cấu trúc này. Bạn chọn cái nào và tại sao?


Chỉnh sửa : Ví dụ hiện cho thấy tình huống phổ biến nhất: id không tuần tự.


1
Cũng có một mảng 2ngày: var people = [[1, 'Joe'],[3, 'Sam'],[8,'Eve']];(mà không cung cấp cho bạn nhanh chóng nhìn lên bởi id, nhưng là một tùy chọn để lưu trữ dữ liệu)
tài khoản

Câu trả lời:


58

Mỗi giải pháp có các trường hợp sử dụng của nó.

Tôi nghĩ rằng giải pháp đầu tiên là tốt nếu bạn đang cố gắng xác định mối quan hệ một-một (chẳng hạn như một ánh xạ đơn giản), đặc biệt nếu bạn cần sử dụng khóa làm khóa tra cứu.

Giải pháp thứ hai cảm thấy mạnh mẽ nhất đối với tôi nói chung và tôi có thể sẽ sử dụng nó nếu tôi không cần khóa tra cứu nhanh:

  • Nó tự mô tả, vì vậy bạn không cần phải phụ thuộc vào bất kỳ ai sử dụng mọi người để biết rằng khóa là id của người dùng.
  • Mỗi đối tượng đều có tính khép kín, điều này tốt hơn cho việc chuyển dữ liệu đi nơi khác - thay vì hai tham số (id và tên) mà bạn chỉ truyền xung quanh mọi người.
  • Đây là một vấn đề hiếm gặp, nhưng đôi khi các giá trị khóa có thể không hợp lệ để sử dụng làm khóa. Ví dụ: tôi đã từng muốn ánh xạ các chuyển đổi chuỗi (ví dụ: ":" thành ">"), nhưng vì ":" không phải là tên biến hợp lệ nên tôi phải sử dụng phương pháp thứ hai.
  • Nó có thể dễ dàng mở rộng, trong trường hợp ở đâu đó dọc theo đường thẳng, bạn cần thêm nhiều dữ liệu hơn cho một số (hoặc tất cả) người dùng. (Xin lỗi, tôi biết về "vì lý do tranh luận" của bạn nhưng đây là một khía cạnh quan trọng.)

Thứ ba sẽ tốt nếu bạn cần thời gian tra cứu nhanh + một số ưu điểm được liệt kê ở trên (chuyển dữ liệu xung quanh, tự mô tả). Tuy nhiên, nếu bạn không cần thời gian tra cứu nhanh thì sẽ rườm rà hơn rất nhiều. Ngoài ra, theo cách nào đó, bạn có nguy cơ bị lỗi nếu id trong đối tượng bằng cách nào đó khác với id ở người .


1
về điểm thứ ba của bạn ở đó. Các thuộc tính của đối tượng có thể được truy cập trong ký hiệu ngoặc để tránh vấn đề đó: var x = {"ab> c ++": "foo"}; alert (x ['ab> c ++']);
nickf

Tôi tin rằng bạn có vấn đề về một vài từ dành riêng với thuộc tính. Nhưng không có vấn đề gì khi bạn đang sử dụng số.
derobert

4
@derobert: Tôi không nghĩ vậy. x = {"delete": 1, "for": 2, "function": 3}; - điều này hợp lệ và có thể được truy cập theo cách tương tự.
nickf

7

Trên thực tế, có một lựa chọn thứ tư:

var people = ['Joe', 'Sam', 'Eve'];

vì các giá trị của bạn xảy ra liên tiếp. (Tất nhiên, bạn sẽ phải cộng / trừ một --- hoặc chỉ đặt undefined làm phần tử đầu tiên).

Cá nhân, tôi sẽ đi với (1) hoặc (3) của bạn, bởi vì đó sẽ là cách nhanh nhất để tra cứu ai đó theo ID ( tệ nhất là O log n ). Nếu bạn phải tìm id 3 trong (2), bạn có thể tra cứu nó theo chỉ mục (trong trường hợp này (4) của tôi là ok) hoặc bạn phải tìm kiếm - O (n).

Làm rõ: Tôi nói O (log n ) là tệ nhất có thể là vì AFAIK và việc triển khai có thể quyết định sử dụng cây cân bằng thay vì bảng băm. Một bảng băm sẽ là O (1), giả sử va chạm tối thiểu.

Chỉnh sửa từ nickf: Tôi đã thay đổi ví dụ trong OP, vì vậy câu trả lời này có thể không còn ý nghĩa nữa. Xin lỗi.

Đã đăng nó

Ok, sau khi chỉnh sửa, tôi sẽ chọn tùy chọn (3). Nó có thể mở rộng (dễ dàng thêm các thuộc tính mới), có tính năng tra cứu nhanh và cũng có thể được lặp lại. Nó cũng cho phép bạn chuyển từ mục nhập trở lại ID, nếu bạn cần.

Tùy chọn (1) sẽ hữu ích nếu (a) bạn cần tiết kiệm bộ nhớ; (b) bạn không bao giờ cần phải chuyển từ đối tượng trở lại id; (c) bạn sẽ không bao giờ mở rộng dữ liệu được lưu trữ (ví dụ: bạn không thể thêm họ của người đó)

Lựa chọn (2) tốt nếu bạn (a) cần duy trì việc đặt hàng; (b) cần lặp lại tất cả các phần tử; (c) không cần tìm kiếm các phần tử theo id, trừ khi nó được sắp xếp theo id (bạn có thể thực hiện tìm kiếm nhị phân trong O (log n ). Lưu ý, tất nhiên, nếu bạn cần sắp xếp nó thì bạn sẽ phải trả một chi phí trên phụ trang.


Tôi hiểu tại sao (1) và (3) nhanh hơn (2), nhưng tôi tự hỏi làm thế nào bạn có được O (log (n)) trong thời gian? Nó không phải là O (1) vì id nên được lưu trữ trong bảng băm?
Nathaniel Reinhart

Tôi cho rằng log n là tệ nhất, vì tôi có thể thấy một triển khai quyết định sử dụng cây cân bằng thay vì bảng băm. Một bảng băm sẽ là O (1) [giả sử các va chạm là nhỏ nhất], tất nhiên.
derobert

ID không phải lúc nào cũng đơn giản như vậy và chúng hiếm khi là IRL tuần tự, đó là lý do tại sao bạn cần chỉ định chúng. Tôi thừa nhận đó là một ví dụ nghèo nàn / mơ hồ.
nickf

+1, câu trả lời của bạn không tệ hơn câu được chấp nhận ... nhưng giải pháp thứ tư của bạn không lưu trữ tất cả dữ liệu, vì vậy đây là tùy chọn thứ tư chính xác: var people = []; people[1] = 'Joe'; people[3] = 'Sam'; people[8] = 'Eve';hoặc dưới dạng mảng hai chiều: var people = []; people[1] = ['Joe', 'Smith']; people[3] = ['Sam', 'Miller']; people[8] = ['Eve', 'Sweet'];(sẽ được truy cập bởi ví dụ: 'people [ 3] [1]; 'hoạt động rất tốt để lưu trữ dữ liệu nội bộ nhanh chóng bên trong bất kỳ lớp nào - tốt, với lớp, tôi có nghĩa là "Dù mới ();" điều mà các Javascript không thích, tôi làm; -) ...
Marcus

... Nhưng điều này chỉ hoạt động nếu id là một số nguyên, nếu không thì giải pháp "cổ điển" 2 (cùng với giải pháp for(var key in object)truy cập nổi tiếng ) là cách thực hiện trong các trường hợp bình thường (nó có O (n / 2)), hoặc giải pháp 3 nếu bạn có một mảng thực sự lớn để truy cập nhanh hơn vì nó có O (1) ... và cuối cùng hãy quên giải pháp 1 ở trên, không chỉ nếu id của bạn là số nguyên, mà còn trong trường hợp id không phải là số nguyên: var people = {'Smith':'Joe', 'Miller':'Sam', 'Sweet': 'Eve' };( bởi vì bạn sẽ cần truy cập đối tượng của mình như thế này: alert(people.Smith);điều này không thực sự tốt!).
Marcus

2

Giả sử dữ liệu sẽ không bao giờ thay đổi, tùy chọn đầu tiên (đối tượng đơn) là tốt nhất.

Sự đơn giản của cấu trúc có nghĩa là nó nhanh nhất để phân tích cú pháp và trong trường hợp các tập dữ liệu nhỏ, hiếm khi (hoặc không bao giờ) thay đổi như tập này, tôi chỉ có thể tưởng tượng rằng nó sẽ được thực thi thường xuyên - trong trường hợp đó, chi phí tối thiểu là cách để đi.


2

Tôi đã tạo một thư viện nhỏ để quản lý các cặp giá trị khóa.

https://github.com/scaraveos/keyval.js#readme

Nó sử dụng

  • một đối tượng để lưu trữ các khóa, cho phép xóa nhanh các thao tác truy xuất giá trị và
  • một danh sách được liên kết để cho phép lặp lại giá trị thực sự nhanh chóng

Hy vọng nó giúp :)


tại sao không thêm một liên kết jsperf?
Janus Troelsen

thư viện đó thật tuyệt vời, công việc tuyệt vời. Chỉ cần tiết kiệm cho tôi rất nhiều thời gian.
Chris Hough

1

Tùy chọn thứ ba là tốt nhất cho bất kỳ ứng dụng hướng tới tương lai nào. Bạn có thể sẽ muốn thêm nhiều trường hơn vào hồ sơ cá nhân của mình, vì vậy tùy chọn đầu tiên không phù hợp. Ngoài ra, rất có thể bạn sẽ có một lượng lớn người cần lưu trữ và sẽ muốn tra cứu hồ sơ một cách nhanh chóng - do đó, đổ chúng vào một mảng đơn giản (như được thực hiện trong tùy chọn số 2) cũng không phải là một ý kiến ​​hay.

Mẫu thứ ba cung cấp cho bạn tùy chọn sử dụng bất kỳ chuỗi nào làm ID, có cấu trúc Người phức tạp và lấy và thiết lập bản ghi người trong một thời gian cố định. Đó chắc chắn là con đường để đi.

Một thứ mà tùy chọn số 3 thiếu là thứ tự xác định ổn định (đó là mặt trái của tùy chọn số 2). Nếu bạn cần điều này, tôi khuyên bạn nên giữ một dãy ID người có thứ tự như một cấu trúc riêng biệt khi bạn cần liệt kê người theo thứ tự. Ưu điểm là bạn có thể giữ nhiều mảng như vậy cho các chuỗi thứ tự khác nhau của cùng một tập dữ liệu.


0

Với ràng buộc của bạn rằng bạn sẽ chỉ có tên là giá trị, tôi sẽ chọn tùy chọn đầu tiên. Nó sạch nhất, ít chi phí nhất và tra cứu nhanh nhất.

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.