Làm thế nào để lấy các khóa mảng trong Javascript?


80

Tôi có một mảng được tạo bằng mã này:

var widthRange = new Array();
widthRange[46] = { min:0,  max:52 };
widthRange[66] = { min:52, max:70 };
widthRange[90] = { min:70, max:94 };

Tôi muốn nhận từng giá trị 46, 66, 90 trong một vòng lặp. Tôi đã thử for (var key in widthRange)nhưng điều này mang lại cho tôi một loạt các thuộc tính bổ sung (tôi giả sử chúng là các hàm trên đối tượng). Tôi không thể sử dụng vòng lặp for thông thường vì các giá trị không tuần tự.


9
Có vẻ như bạn có dữ liệu, mặc dù nó có các phím số, nhưng không thực sự là dữ liệu mảng. Tôi sẽ xem xét việc sử dụng một đối tượng thông thường ở đây.
Quentin

@Quentin Nó được gọi là một mảng thưa thớt. Về hiệu suất, tốt nhất là sử dụng một mảng thay vì một đối tượng ở đây. Ngoài ra, hiệu suất-khôn ngoan, câu trả lời tốt nhất là thậm chí không liệt kê: Array.prototype.forEach. Gọi Object.keystrên một mảng là không hiệu quả vì trình duyệt không tối ưu hóa cho nó. for(var key in array)là không tốt vì nó đi ngang qua nguyên mẫu và xâu chuỗi từng phím số mà nó gặp phải (chuyển đổi double thành base10 rất chậm). forEachTuy nhiên, được thiết kế chính xác với mục đích lặp mảng thưa thớt và sẽ cung cấp hiệu suất tuyệt vời mã của bạn so với các giải pháp khác
Jack Giffin

Câu trả lời:


96

Bạn cần gọi hasOwnPropertyhàm để kiểm tra xem thuộc tính có thực sự được xác định trên chính đối tượng hay không (trái ngược với nguyên mẫu của nó), như sau:

for (var key in widthRange) {
    if (key === 'length' || !widthRange.hasOwnProperty(key)) continue;
    var value = widthRange[key];
}

Lưu ý rằng bạn cần kiểm tra riêng cho length.
Tuy nhiên, bạn không nên sử dụng một mảng ở đây; bạn nên sử dụng một đối tượng thông thường. Tất cả các đối tượng Javascript hoạt động như một mảng kết hợp.

Ví dụ:

var widthRange = { };  //Or new Object()
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

2
Có thật không? Chà, tôi học được điều gì đó mỗi ngày ở đây, và thường là từ bạn SLaks :-) Tôi ngạc nhiên khi Array theo dõi các chỉ mục được thiết lập rõ ràng như vậy. Có lẽ tôi không nên như vậy.
Pointy

@SLaks: Cảm ơn, thay đổi nó thành một đối tượng có vẻ là giải pháp tốt nhất. Một câu hỏi nữa: có cách nào để lặp lại theo thứ tự ngược lại không?
DisgruntledGoat

Không; bạn sẽ cần phải tự làm.
SLaks

2
Tại sao bạn cần kiểm tra riêng cho length? Không được đánh dấu là [[DontEnum]]trong tất cả các trình duyệt?
Roatin Marth

@Roatin: Tôi không biết. Cẩn tắc vô ưu.
SLaks

84

Các khóa được xâu chuỗi có thể được truy vấn với Object.keys(array).


4
Điều này trả về các khóa dưới dạng chuỗi, không phải số, để mọi người biết.
Hutch Moore

Giải pháp tốt nhất ... TY
Carlos Galeano

Đã làm nên ngày của tôi! : D <3 Cái này giải quyết được mà array.keys()không trả lại được gì.
Christopher Stock

18

Nếu bạn đang thực hiện bất kỳ loại thao tác hoặc kiểm tra mảng / tập hợp nào, tôi thực sự khuyên bạn nên sử dụng Underscore.js . Nó nhỏ, được thử nghiệm tốt và sẽ giúp bạn tiết kiệm số ngày / tuần / năm đau đầu về javascript. Đây là chức năng phím của nó:

Chìa khóa

Lấy tất cả các tên thuộc tính của đối tượng.

_.keys({one : 1, two : 2, three : 3});
=> ["one", "two", "three"]

4

Giả sử mảng của bạn trông giống như arr = [ { a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }, { a: 7, b: 8, c: 9 } ](hoặc có thể là các phím khác) bạn có thể làm

arr.map((o) => {
    return Object.keys(o)
}).reduce((prev, curr) => {
    return prev.concat(curr)
}).filter((col, i, array) => {
    return array.indexOf(col) === i
});

["a", "b", "c"]


3
for (var i = 0; i < widthRange.length; ++i) {
  if (widthRange[i] != null) {
    // do something
  }
}

Bạn thực sự không thể chỉ lấy các khóa bạn đã đặt vì đó không phải là cách Mảng hoạt động. Khi bạn đặt phần tử 46, bạn cũng có từ 0 đến 45 được đặt (mặc dù chúng là rỗng).

Bạn luôn có thể có hai mảng:

var widthRange = [], widths = [], newVal = function(n) {
  widths.push(n);
  return n;
};
widthRange[newVal(26)] = { whatever: "hello there" };

for (var i = 0; i < widths.length; ++i) {
  doSomething(widthRange[widths[i]]);
}

chỉnh sửa tốt, có thể là tôi ướt hết cả người ở đây ...


Mảng chỉ theo dõi độ dài, chúng không đi qua và tạo ra nhiều đối tượng. Về cơ bản, khi một chỉ mục mới được tìm thấy, nó sẽ kiểm tra xem chỉ mục mới đó có lớn hơn độ dài hay không ... nếu chỉ mục mới đó trở thành độ dài mới. Do đó nó có thể chỉ nhận được các chỉ số bạn đã định nghĩa với một phi trong vòng lặp
Bob


1

Ví dụ ban đầu của bạn phù hợp với tôi:

<html>
<head>
</head>
<body>
<script>
var widthRange = new Array();
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

var i = 1;
for (var key in widthRange)
{
    document.write("Key #" + i + " = " + key + "; &nbsp;&nbsp;&nbsp; min/max = " + widthRange[key].min + "/" + widthRange[key].max + "<br />");
    i++;
}
</script>
</html>

Kết quả trong trình duyệt (Firefox 3.6.2 trên Windows XP):

Key #1 = 46;     min/max = 0/52
Key #2 = 66;     min/max = 52/70
Key #3 = 90;     min/max = 70/94

2
Tôi cá là anh ta đang nhập Nguyên mẫu hoặc thứ gì đó tương tự. Nói chung, những vòng lặp "trong" đó là rủi ro.
Pointy

Tôi đang sử dụng jQuery nhưng cũng có Joomla sử dụng mootools, một cái gì đó đang thêm các thuộc tính đó.
DisgruntledGoat

1

Tôi nghĩ bạn nên sử dụng một Object ( {}) chứ không phải một mảng ( []) cho việc này.

Một tập hợp dữ liệu được liên kết với mỗi khóa. Nó hét lên vì sử dụng một đồ vật. Làm:

var obj = {};
obj[46] = { sel:46, min:0,  max:52 };
obj[666] = { whatever:true };

// This is what for..in is for
for (var prop in obj) {
  console.log(obj[prop]);
}

Có thể một số công cụ tiện ích như thế này có thể giúp:

window.WidthRange = (function () {
  var obj = {};
  return {
    getObj: function () {return obj;}
    , add: function (key, data) {
        obj[key] = data;
        return this; // enabling chaining
      }
  }
})();

// Usage (using chaining calls):
WidthRange.add(66, {foo: true})
.add(67, {bar: false})
.add(69, {baz: 'maybe', bork:'absolutely'});

var obj = WidthRange.getObj();
for (var prop in obj) {
  console.log(obj[prop]);
}

0

Có vẻ hiệu quả.

var widthRange = new Array();
widthRange[46] = { sel:46, min:0,  max:52 };
widthRange[66] = { sel:66, min:52, max:70 };
widthRange[90] = { sel:90, min:70, max:94 };

for (var key in widthRange)
{
    document.write(widthRange[key].sel + "<br />");
    document.write(widthRange[key].min + "<br />");
    document.write(widthRange[key].max + "<br />");
}

0

Tôi đã viết một hàm hoạt động tốt với mọi phiên bản của Đối tượng (Mảng là những cái đó).

Object.prototype.toArray = function()
{
    if(!this)
    {
      return null;
    }

    var c = [];

    for (var key in this) 
    {
        if ( ( this instanceof Array && this.constructor === Array && key === 'length' ) || !this.hasOwnProperty(key) ) 
        {
            continue;
        }

        c.push(this[key]);
    }

    return c;
};

Sử dụng:

var a   = [ 1, 2, 3 ];
a[11]   = 4;
a["js"] = 5;

console.log(a.toArray());

var b = { one: 1, two: 2, three: 3, f: function() { return 4; }, five: 5 };
b[7] = 7;

console.log(b.toArray());

Đầu ra:

> [ 1, 2, 3, 4, 5 ]
> [ 7, 1, 2, 3, function () { return 4; }, 5 ]

Nó có thể hữu ích cho bất kỳ ai.


2
Mở rộng một nguyên mẫu mà bạn không tạo ra là một ý tưởng rất tồi, bất kể bạn nghĩ nó thuận tiện đến mức nào.
doug65536

0

... ????

Ngoài ra, nếu bạn có một danh sách các mục bạn muốn sử dụng ...

var range = [46, 66, 90]
    , widthRange=[]
    , write=[];

    widthRange[46] = { min:0, max:52 }; 
    widthRange[66] = { min:52, max:70 }; 
    widthRange[90] = { min:70, max:94 }; 

for(var x=0; x<range.length; x++){var key, wr;

    key = range[x];

    wr = widthRange[key] || false;

    if(wr===false){continue;}

    write.push(['key: #',key, ', min: ', wr.min, 'max:', wr.max].join(''));

    }

0

Đối với dữ liệu đầu vào của bạn:

let widthRange = new Array()
widthRange[46] = { min:0,  max:52 }
widthRange[61] = { min:52, max:70 }
widthRange[62] = { min:52, max:70 }
widthRange[63] = { min:52, max:70 }
widthRange[66] = { min:52, max:70 }
widthRange[90] = { min:70, max:94 }

Cách tiếp cận so sánh:

const relevantKeys = [46,66,90]
const relevantValues = Object.keys(widthRange)
    .filter(index => relevantKeys.includes(parseInt(index)))
    .map(relevantIndex => widthRange[relevantIndex])

Object.keysđể lấy chìa khóa, sử dụng parseIntđể truyền chúng dưới dạng số. filterđể chỉ nhận được những thứ bạn muốn. mapđể xây dựng một mảng từ đối tượng ban đầu của chỉ các chỉ số bạn đang theo dõi, vì Object.keyssẽ mất các giá trị đối tượng.

Gỡ lỗi:

console.log(widthRange)
console.log(relevantKeys)
console.log(relevantValues)

0

Câu hỏi này khá cũ, nhưng ngày nay bạn có thể sử dụng forEach, cách này hiệu quả và sẽ giữ lại các khóa dưới dạng số:

let keys = widthRange.map((v,k) => k).filter(i=>i!==undefined))

Điều này lặp lại qua widthRange và tạo một mảng mới với giá trị của các khóa, sau đó lọc ra tất cả các vị trí ghép bằng cách chỉ lấy các giá trị đã được xác định.

(Ý tưởng tồi, nhưng đối với sự nhanh chóng: Nếu vị trí 0 luôn trống, điều đó có thể được rút ngắn thành filter(i=>i) hoặcfilter(Boolean)

Và, nó có thể kém hiệu quả hơn, nhưng các con số có thể được truyền với let keys = Object.keys(array).map(i=>i*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.