JavaScript преобразует ключи с числовыми строками в Numbers… но Object.keys() не

#javascript #ecmascript-6

#javascript #ecmascript-6

Вопрос:

Внешний API возвращает результат JSON следующего вида:

 {
    "data": {
        "1.0": 'foo',
        "2.3": 'bar',
        "3.6": 'baz'
    }
}
  

Здесь ключи "1.0", "2.3", "3.6" действительно следует воспринимать как строки, обозначающие дискретную категоризацию, не как значения вдоль непрерывной оси. Поэтому для этого API совершенно допустимо возвращать эти ключи в виде строк.

Однако… (вы чувствуете, что это приближается, не так ли?)

В клиенте JS мне нужно выполнить итерацию по этим ключам, и вот тут возникает проблема:

  • движок JS браузера автоматически преобразовал все эти ключи в Number
  • использование Object.keys(myObject.data) возвращает … строки!
  • следовательно, следующее вообще не работает, как вы можете видеть:

 let myObject = {
  "data": {
    "1.0": 'foo',
    "2.3": 'bar',
    "3.6": 'baz'
  }
}

console.log(myObject.data)
for (let k in Object.keys(myObject.data)) {
  console.log(k, myObject.data[k])
}

// {
//     1.0: 'foo',
//     2.3: 'bar',
//     3.6: 'baz
// }
// "1.0" undefined
// "2.3" undefined
// "3.6" undefined  

Похоже, что здесь у нас есть две противоречивые вещи: во-первых, ключи объекта преобразуются в числа, но в то же время Object.keys() возвращает строки вместо чисел.

Есть ли подходящий способ решить эту проблему?

В идеале я бы хотел, чтобы фактические ключи объекта оставались строками, как и должно быть. Преобразование значений из Object.keys() в Numbers привело бы к довольно громоздким обходным путям, поскольку API может (и иногда возвращает) «реальные» строки в качестве ключей (например { "red": 'foo', "blue": 'bar' } .

Комментарии:

1. используйте цикл for of

2. console.log(k, myObject.data[k]) выводит // 0 undefined 1 undefined и не "1.0" undefined выводит. Проверьте ответ @ CodeManiac . Object.keys() возвращает массив. Вы можете просто использовать for (let k in myObject.data){} вместо

3. » движок JS браузера автоматически преобразовал все эти ключи в Number » — эм, нет, что заставляет вас так думать? Ключи объекта всегда являются строками (или символами), никогда числами.

Ответ №1:

Ваша проблема в for in

for in пытается получить доступ к ключам в массиве, созданном Object.keys(obj.data) , который на самом деле является индексом

 let obj = {"data": {"1.0": 'foo',"2.3": 'bar',"3.6": 'baz'}}

Object.keys(obj.data).forEach(e=>{
  console.log(typeof e)
})

//You can simply drop of Object.keys 

for (let k in obj.data) {
  console.log(k, obj.data[k])
}  

Комментарии:

1. Можете ли вы сослаться на авторитетный источник для этого?

2. @jayarjo итерация массива с использованием for в

3. Ваш краткий способ формулирования ответов смутил меня. Конечно, это индексы, но массива, а не объекта.

4. Будет полезно, если вы добавите for (let k in myObject.data) { } к ответу

5. @jayarjo for in tries to access keys in array created by Object.keys(obj.data) which is actually index я указал здесь массив

Ответ №2:

Просто не используйте Object.keys :

 let myObject = {
  "data": {
    "1.0": 'foo',
    "2.3": 'bar',
    "3.6": 'baz'
  }
}

console.log(myObject.data)
for (let k in myObject.data) {
  console.log(k, myObject.data[k])
}  

Некоторое объяснение:

Object.keys делает то, что говорит — извлекает ключи из переданного объекта и возвращает их в виде массива (в вашем случае это было бы: [ "1.0", "2.3", "3.6"] ). Поэтому, когда вы пытаетесь выполнить цикл по этому с помощью for..in , вы фактически выполняете цикл по результирующему массиву, вместо фактического объекта и key переменной будет получен индекс соответствующего элемента из массива ( 0 для "1.0" , 1 для "2.3" и т.д.). Именно так for..in работает. Если вы хотите вместо этого перебирать значения массива, вы могли бы использовать for..of как другой вариант. Или в вашем случае, как я упоминал выше, просто не используйте Object.keys .

Ответ №3:

Проблема в for..in цикле, попробуйте for..of решить эту проблему. for..in Цикл повторит все перечислимые свойства самого объекта и те, которые объект наследует от прототипа своего конструктора.

В то время как for..of , с другой стороны, его в основном интересуют значения итеративных объектов, в данном случае это массив, возвращаемый Object.keys() вызовом.

 var myObject = {
    "data": {
        "1.0": 'foo',
        "2.3": 'bar',
        "3.6": 'baz'
    }
}
console.log(myObject.data)
for (let k of Object.keys(myObject.data)) {
    console.log(k, myObject.data[k])
}  

Здесь, когда вы выполняете итерацию по Object.keys(myObject.data) , рассматриваются индексы (ключи объекта array) возвращаемого массива вместо фактических значений myObject.data массива.

Вот различие с небольшим примером:

 var arr = [10, 20, 30];
console.log("**for - in loop**")
//logs indices 0, 1, 2
for (i in arr){
  console.log(i);
}
console.log("**for - of loop**")
//logs values in the array 10, 20, 30
for (i of arr){
  console.log(i);
}  

Комментарии:

1. Это недопустимое различие между for...in и for..of .