最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Javascript - maintain key order when going from object -> array - Stack Overflow

programmeradmin2浏览0评论

I know key order isn't guaranteed in JS objects, however, my data structure comes from a backend for which I have no control over. Is there anything I can do to preserve the key order when going from:

var obj = {
   foo: 'bar',
   bar: 'foo' 
};

to:

Object.keys(obj); // hoping for ['foo', 'bar']

It's of the utmost importance that I keep the key order unfortunately...

I know key order isn't guaranteed in JS objects, however, my data structure comes from a backend for which I have no control over. Is there anything I can do to preserve the key order when going from:

var obj = {
   foo: 'bar',
   bar: 'foo' 
};

to:

Object.keys(obj); // hoping for ['foo', 'bar']

It's of the utmost importance that I keep the key order unfortunately...

Share Improve this question asked Jul 14, 2015 at 14:12 benhowdle89benhowdle89 37.5k74 gold badges207 silver badges339 bronze badges 20
  • 4 How do you expect to maintain key order when the key order itself is non-deterministic? :S – Dan Commented Jul 14, 2015 at 14:13
  • Well property ordering really isn't guaranteed, unfortunately. The order will be whatever it happens to be. Why is it important? – Pointy Commented Jul 14, 2015 at 14:13
  • @DanPantry The object is parsed from JSON sent by a Python backend...which maybe does support key order? – benhowdle89 Commented Jul 14, 2015 at 14:14
  • 1 It doesn't matter what Python does. – Pointy Commented Jul 14, 2015 at 14:15
  • Once the object crosses the JSON barrier, there is no guarantee on the order of your properties (which makes sense because you cant' really 'order' properties). It might make more sense to put each of your properties into its own object in an array, and send that instead. Arrays preserve key order. Even if you could maintain the order of an object when its converted to an array, the original order of the object would be non-deterministic anyway, so you would never be able to define the order of the object anyway. – Dan Commented Jul 14, 2015 at 14:16
 |  Show 15 more comments

4 Answers 4

Reset to default 7

No. As you wrote:

I know key order isn't guaranteed in JS objects

If you want the order you need to use an array. If you have an object, then there is no defined order to the properties.

This is doable since ES2015.

Stefan Judis explains it really well here https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/


Here's how it works:

  • Number-like keys always come first, no matter when you inserted them. They are sorted numerically.

  • Strings come next. They are sorted chronologically. Insert "foo" after "bar", and it'll stay that way!

  • Symbols come last. They are sorted chronologically, too.

const obj = {
  '2': 'integer: 2',
  'foo': 'string: foo',
  '01': 'string: 01', // "01" counts as a string. Only "1" is a number-like key!
  1: 'integer: 1',
  [Symbol('first')]: 'symbol: first'
};

The resulting order of those keys is "1", "2", "foo", "01", Symbol(first).

The integers were moved to the start and sorted numerically. Next are strings, in the same order as they were ("foo", then "01"). Symbols are moved to the end, but otherwise keep their order.

The JSON specification indicates that

An object is an unordered collection of zero or more name/value pairs...

Strictly speaking, the literal JSON text has an order, of course: the text isn't going to suddenly scramble itself. But your desired behavior -- an ordered set of name/value pairs -- is not a construct that JSON provides. You want an object notation to provide semantic meaning beyond the semantics that are required by the JSON specification.

In order words, doing this in JSON

{
    "foo": "baz",
    "bar": "egg" 
}

is description of an unordered set. If you intended for this to describe an ordered set, you're not following the rules of JSON. The data happens to be ordered (because that's how character sequences work), but JSON semantics freely allow an implementation to disregard the order of name/value pairs completely when considering the data present in the input string.

You could write code that operates directly on the JSON input string, to parse the text in such a way that the input order of the keys is remembered somewhere, but a JSON parser implementation is not required to supply that functionality to you.

The correct JSON-idiomatic solution would be to provide an array of key names in the JSON response:

{
    data: {
        "foo": "baz",
        "bar": "egg" 
    },
    order: ["foo", "bar"]
}

Obviously, if you don't control the response, you can't do that. By choosing the represent the data in an object, the author of the response has asserted that order of keys is purely arbitrary. If the author of the JSON intends to assert order of key entries, that assertion is external to the rules of JSON.

As a practical matter, most JavaScript engines will usually choose to report key names in the order their properties were created. However, this behavior is not required by any specification (and is sometimes inconsistent in edge cases), and so could legally differ between engines and versions.

ECMA-262 does not specify enumeration order. The de facto standard is to match insertion order.

No guarantees are given though on the enumeration order for array indices (i.e., a property name that can be parsed as an integer), because insertion order for array indices would incur significant memory overhead.

added jsfiddle example.

var obj1 = {
   a: 'test1',
   b: 'test2' 
};

var obj2 = {
   2: 'test1',
   1: 'test2' 
};
// max 32bit unsigned is 2,147,483,647
var obj3 = {
   2147483649: 'test1',
   2147483648: 'test2' 
};
// max 64bit unsigned is 9,223,372,036,854,775,807
var obj4 = {
   9223372036854770: 'test1',
   9223372036854768: 'test2',
};

// != number < 2,147,483,647, order is not changed
console.log(Object.keys(obj1));
// < 2,147,483,647, order is changed
console.log(Object.keys(obj2));
// > 2,147,483,647, order is not changed in Firefox, but changed in Chrome
console.log(Object.keys(obj3));
// > 9,223,372,036,854,775,807, order is not changed in neither Firefox or Chrome
console.log(Object.keys(obj4));

Which means Chrome's JavaScript engine V8 will order any 64bit unsigned numbers, while Firefox's JavaScript engine SpiderMonkey will order any 32bit unsigned numbers.

发布评论

评论列表(0)

  1. 暂无评论