-2

it should be quick and simple but NOT :(

I have 2 array of objects which looks like this:

var obj1 = {
 1: "Available"
 3: "Available - Sleeps"
}
var obj2 = {
 2: "Not Available"
 4: "Holidays"
 6: "Sick"
}

I would like to have a 3rd object whihc is the merge of the 2 mantaining the original order, like this:

var obj3 = {
 1: "Available"
 3: "Available - Sleeps"
 2: "Not Available"
 4: "Holidays"
 6: "Sick"
}

Unfortunately it always order the items by key so I get this :( :

var obj3 = {
 1: "Available"
 2: "Not Available"
 3: "Available - Sleeps"
 4: "Holidays"
 6: "Sick"
}

I have tried different ways to merge the object but the order never stays the same, I have tried:

$.extend(obj3, obj1, obj2);
obj3 = Object.assign(obj1, obj2);
obj3 = {...obj1,...obj2}

Any ideas anyone :)

Thanks

Daniele
  • 125
  • 11

4 Answers4

0

Object property enumeration order isn't reliable.

Use Map instead since it preserves insertion order:

const map1 = new Map([
  [1, "Available"],
  [3, "Available - Sleeps"]
])

const map2 = new Map([
  [2, "Not Available"],
  [4, "Holidays"],
  [6, "Sick"]
])

const map3 = new Map([...map1, ...map2])

console.log([...map3])

// Also you can easily convert and object to dictionary:
const fromPairs = pairs => {
  const obj = {}

  for (let [key, value] of pairs)
    obj[key] = value

  return obj
}

const obj = fromPairs(map3)

console.log(obj)

See this other Q&A to get more insights about object property enumeration order: Does JavaScript Guarantee Object Property Order?

Matías Fidemraizer
  • 59,064
  • 16
  • 107
  • 181
0

You cannot create an object whose properties will be enumerated in the order you've said you want with those property keys. As of ES2015, object properties do have an order (officially specified only for some operations, but all modern JavaScript engines also apply it to the unspecified operations). One aspect of that order is that properties whose names are integer indexes as defined by the spec* are visited in numeric order. Your 1, 2, 3, etc. properties fit the definition of integer index keys, and so the combined object's properties will be enumerated in 1, 2, 3, ... order.

Instead, use an array:

const obj3 = [];
Object.keys(obj1).forEach(key => { obj3.push({key, value: obj1[key]}); });
Object.keys(obj2).forEach(key => { obj3.push({key, value: obj2[key]}); });

var obj1 = {
 1: "Available",
 3: "Available - Sleeps"
};
var obj2 = {
 2: "Not Available",
 4: "Holidays",
 6: "Sick"
};
const obj3 = [];
Object.keys(obj1).forEach(key => { obj3.push({key, value: obj1[key]}); });
Object.keys(obj2).forEach(key => { obj3.push({key, value: obj2[key]}); });
console.log(obj3);
.as-console-wrapper {
  max-height: 100% !important;
}

...although technically, if you made those names not integer indexes, you could create an object with properties that would be numerated in that order:

var obj3 = {};
Object.keys(obj1).forEach(key => { obj3["x" + key] = obj1[key]; });
Object.keys(obj2).forEach(key => { obj3["x" + key] = obj2[key]; });

var obj1 = {
 1: "Available",
 3: "Available - Sleeps"
};
var obj2 = {
 2: "Not Available",
 4: "Holidays",
 6: "Sick"
};
var obj3 = {};
Object.keys(obj1).forEach(key => { obj3["x" + key] = obj1[key]; });
Object.keys(obj2).forEach(key => { obj3["x" + key] = obj2[key]; });
console.log(obj3);

But I don't recommend it.


* Defined here:

An integer index is a String-valued property key that is a canonical numeric String (see 7.1.16) and whose numeric value is either +0 or a positive integer ≤ 253-1.

T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • Thank you some much all, at the end I have used this one: Object.keys(obj1).forEach(key => { obj3.push({key, value: obj1[key]}); }); which works perfectly :) @sjahan - the array contains: - Availability Types - Unavailability Types and the Acceptance Criteria wants All Availability first followed by Unavailability...that's why I needed to keep the order – Daniele Jun 12 '18 at 16:06
0

Maybe This is helpful.

var obj1 = {
 1: "Available",
 3: "Available - Sleeps"
}
var obj2 = {
 2: "Not Available",
 4: "Holidays",
 6: "Sick"
}

const merged = Object.values(obj1).concat(Object.values(obj2)).reduce((result, value, index)=>{result[index] = value; return result},{})

console.log(merged)
0

If you really want to keep the order of the keys, here is what I would do if I were you:

var obj1 = {
 1: "Available",
 3: "Available - Sleeps"
};
var obj2 = {
 2: "Not Available",
 4: "Holidays",
 6: "Sick"
}

Object.defineProperty(Object.prototype, 'orderedKeys', {
  get: function () {
    return this.hasOwnProperty('orderedKeys') ? this.orderedKeys : Object.keys(this);
  }, 
  enumerable: false
});

Object.defineProperty(Object.prototype, 'concat', {
  value: function (obj) {
    return {
      ...this, 
      ...obj,
      orderedKeys: this.orderedKeys.concat(Object.keys(obj))
    };
  }, 
  enumerable: false
});

var obj3 = obj1.concat(obj2);
console.log(obj3);
for(const key of obj3.orderedKeys) {
  console.log(key, obj3[key]);
}

This way, you can always keep an ordered version of your keys in orderedKeys so that you can use it later.

sjahan
  • 5,174
  • 3
  • 15
  • 34