0

Goal: to create an object with an specific order defined by an object, but only when the fields exists on the input data.

What I have and done:

This object defines the order:

const fieldsOrder = {
  token: undefined,
  agentID: undefined,
  agentSequence: undefined,
  allOptions: undefined
}

This is the body request that I need to sort:

const request = {
  allOptions: false,
  agentSequence: 6,
  agentID: 123,
  token: 'test'
}

The sorted object is sortedObject

const sortedObject = Object.assign(fieldsOrder, request);
console.log(sortedObject);

{
  agentID: 123,
  agentSequence: 6,
  allOptions: false,
  token: 'test'
}

Not working.

I was trying what I found here: Changing the order of the Object keys....

Here are some tests:

// This object defines de order:

const fieldsOrder = {
  token: undefined,
  agentID: undefined,
  agentSequence: undefined,
  allOptions: undefined
}

// 1st case: all fields
// This is the body request that I need to sort:
const request = {
  allOptions: true,
  agentSequence: 6,
  agentID: 123,
  token: 'test',
}

// The sorted object is `sortedRequest`

const sortedRequest = Object.assign(fieldsOrder, request);

// WRONG order...
console.log(sortedRequest); 

/*
I expected this order:
{
  token: 'test'
    agentID: 123,
  agentSequence: 6,
  allOptions: false
}
*/

/*************************************/

// 2nd case: some fields

const requestShort = {
  allOptions: true,
  agentID: 123,
  token: 'test',
}

const sortedRequest2 = Object.assign(fieldsOrder, requestShort);

// WRONG order...
console.log(sortedRequest2); 

/*
I expected this order:
{
  token: 'test'
    agentID: 123,
  allOptions: false
}
*/

How could I fix it? I need to order the request by fieldsOrder but only using the fields on the requestobject.

pmiranda
  • 7,602
  • 14
  • 72
  • 155
  • 2
    `I need to order the request by fieldsOrder`, why? – Olian04 Nov 13 '20 at 15:58
  • I will send that object to an stupid API (SOAP, badly designed) that needs a certain order or it fails. – pmiranda Nov 13 '20 at 16:01
  • You could just create a different object with the keys in the correct order and just set their values to the values from the out of order object. It’s not a fancy solution but it will work. You can even put it in a function to reuse it. – imvain2 Nov 13 '20 at 16:04
  • The order of fields have only recently become deterministic, if you run code that depends on field order on an old machine or in an old browser, then it will fail randomly. – Olian04 Nov 13 '20 at 16:05
  • Keep in mind that objects are not a good option if you need an ordered collection of items. The ideal would be either an array or a Map, where the order is guaranteed. See more: https://stackoverflow.com/a/5525820/3496534 – Ruan Martinelli Nov 13 '20 at 16:05
  • But that will be hard with the 2nd case that I put in the fiddle. The request will not always have the same fields, but in case thos fields exists, they need a specific order – pmiranda Nov 13 '20 at 16:06
  • @RuanMartinelli I need to send an object, and what I have as input is an object too. I tried to define the sort template as an array but I have some problems with indexes (Eslinter was giving me errors) – pmiranda Nov 13 '20 at 16:07
  • So have an array of keys, loop over it. If the key exists add it to a new object. Weird thing is object order was really never guaranteed so a bit weird order would matter. – epascarello Nov 13 '20 at 16:07
  • There are several other answers on that question besides the one mentioning `Object.assign`... do any of those work? – Heretic Monkey Nov 13 '20 at 16:12
  • Does this answer your question? [Sort JavaScript object by key](https://stackoverflow.com/questions/5467129/sort-javascript-object-by-key) – Heretic Monkey Nov 13 '20 at 16:14
  • Thanks to everyone, I'm reading each answer and comment now. – pmiranda Nov 13 '20 at 16:16
  • Similar question you can see [here](https://stackoverflow.com/questions/56996782/javascript-sorting-objects-not-arrays-in-descending) – Naren Nov 13 '20 at 16:22

4 Answers4

2

const keys = ["token", "agentID", "agentSequence", "allOptions"]

function sortRequest(request) {
  return keys.reduce((sortedRequest, key) => {
    if (key in request) sortedRequest[key] = request[key]
    return sortedRequest
  }, {})
}


console.log(sortRequest({
  allOptions: false,
  agentSequence: 6,
  agentID: 123,
  token: 'test'
}))
Tibebes. M
  • 6,940
  • 5
  • 15
  • 36
hackape
  • 18,643
  • 2
  • 29
  • 57
1

You could use Object#entries to get an array of key-value tuples, then sort that array based on the index of each key in the "targetOrder" object, then turn those key-value tuples back into an object using Object#fromEntries. However the order of fields have only recently become deterministic, if you run code that depends on field order on an old machine or in an old browser, then it will fail randomly.

const targetOrder = Object.keys({
  token: undefined,
  agentID: undefined,
  agentSequence: undefined,
  allOptions: undefined
});

const req = {
  allOptions: true,
  agentSequence: 6,
  agentID: 123,
  token: 'test',
}

const orderedReq = Object.fromEntries(
  Object.entries(req)
    .sort(([k1], [k2]) =>
      targetOrder.indexOf(k1) - targetOrder.indexOf(k2)
    ),
);

console.log(orderedReq);
Olian04
  • 6,480
  • 2
  • 27
  • 54
  • Hm, nice, it seemds that JSfiddle is sorting the output console.log, jesus, spend hours looking the jsfiddle console, and not the Chrome console. – pmiranda Nov 13 '20 at 16:12
  • @pmiranda I was thinking that might have been the case, but since I'm not sure what system/environment you where on I couldn't be sure. – Olian04 Nov 13 '20 at 16:13
0

You can iterate trough the keys of the request object and assign every key that exist in fieldsOrder. After that just simply remove all unset fields.

const fieldsOrder = {
  token: undefined,
  agentID: undefined,
  agentSequence: undefined,
  allOptions: undefined
}

const request = {
  allOptions: false,
  agentSequence: 6,
  agentID: 123,
  token: 'test'
}

Object.keys(request)
      .filter(key => key in fieldsOrder)
      .forEach(key => fieldsOrder[key] = request[key])
Object.keys(fieldsOrder)
      .filter(key => fieldsOrder[key] === undefined)
      .forEach(key => delete fieldsOrder[key])

With this solution you do not need to modify your existing objects or creating any string array.

Martin Godzina
  • 1,470
  • 11
  • 17
0

So have an array of keys and loop over it to make a new object.

var fields = ['foo', 'bar', 'baz'];

var data1 = {
  baz: 456,
  foo: 123
};

var data2 = {
  baz: 987,
  foo: 765,
  bar: undefined
};

// only returned if has value
function test1(data) {
  return fields.reduce((o, k) => {
    if (data[k] !== undefined) o[k] = data[k];
    return o;
  }, {});
}

// return everything
function test2(data) {
  return fields.reduce((o, k) => (o[k] = data[k], o), {});
}

// return if it has the key
function test3(data) {
  return fields.reduce((o, k) => {
    if (k in data) {
      o[k] = data2[k];
    }
    return o;
  }, {});
}

console.group('test1')
console.log(test1(data1));
console.log(test1(data2));
console.groupEnd('test1')

console.group('test2')
console.log(test2(data1));
console.log(test2(data2));
console.groupEnd('test2')

console.group('test3')
console.log(test3(data1));
console.log(test3(data2));
console.groupEnd('test3')
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • 1
    This would fail if `undefined` is a valid value for a required field. – Olian04 Nov 13 '20 at 16:14
  • Yes, some data could be: `data = { id: 1, token: null, status: false, name: undefined, code: '' }` – pmiranda Nov 13 '20 at 16:18
  • @Olian04 I made an assumption it was just items that are defined. Lack of data of what values could be means I HAVE TO GUESS. Hence why these details need to be given. So if it can be undefined, remove the check. I altered it to remove the undefined check – epascarello Nov 13 '20 at 16:20