1

I have the following sample JSON coming from a server. Duplicate objects are being internally referred to by an id (see the JSON below).

[
  { "id": 1,
    "agent": {
      "id": 1,
      "firstName": "gghg",
      "lastName": "gh",
      "phone": "4543534",
      "admin": true
    },
    "user":"agent@gmail.com"
  },
  { "id": 2,
    "agent": 1, // here I want the full object and not the Id
    "user":"agent1@gmail.com"
  }
]

Question: How do I resolve the objects referred to in this fashion given a random JSON object?

(For instance, for the sample JSON above, I will have the below output:)

[
  { "id": 1,
    "agent": {
      "id": 1,
      "firstName": "gghg",
      "lastName": "gh",
      "phone": "4543534",
      "admin": true
    },
    "user":"agent@gmail.com"
  },
  { "id": 2,
    "agent": {
      "id": 1,
      "firstName": "gghg",
      "lastName": "gh",
      "phone": "4543534",
      "admin": true
    },
    "user":"agent1@gmail.com"
  }
]
kukkuz
  • 41,512
  • 6
  • 59
  • 95

5 Answers5

2

Basically a single loop proposal, which collects unresolved links and if found the it replaces the open parts with the object.

var data = [{ "id": 1, "agent": { "id": 1, "firstName": "gghg", "lastName": "gh", "phone": "4543534", "admin": true }, "user": "agent@gmail.com" }, { "id": 2, "agent": 1, "user": "agent1@gmail.com" }];

data.forEach(function (a) {
    if (typeof a.agent === 'object') {
        this[a.agent.id] = this[a.agent.id] || {};
        this[a.agent.id].data = a.agent;
        this[a.agent.id].update && this[a.agent.id].update.forEach(function (b) {
            b.agent = a.agent;
        });
        return;
    } 
    this[a.agent] = this[a.agent] || {};
    if (this[a.agent].data) {
        a.agent = this[a.agent].data;
        return;
    }
    this[a.agent].update = this[a.agent].update || [];
    this[a.agent].update.push(a);
}, Object.create(null));

console.log(data);

Edit, a more generic version for unknown property references.

var data = [
        { id: 1, agent: { id: 1, firstName: "gghg", lastName: "gh", phone: "4543534", admin: true }, user: "agent@gmail.com", abc: 2 },
        { id: 2, agent: 1, user: "agent1@gmail.com", abc: { id: 2, text: 'blabla' } },
        { id: 3, agent: { id: 1, firstName: "gghg", lastName: "gh", phone: "4543534", admin: true }, user: "agent@gmail.com" },
    ];

data.forEach(function (a) {
    Object.keys(a).forEach(function (k) {
        if (typeof a[k] === 'object' && 'id' in a[k]) {
            this[a[k].id] = this[a[k].id] || {};
            this[a[k].id].data = a[k];
            this[a[k].id].update && this[a[k].id].update.forEach(function (b) {
                b[k] = a[k];
            });
            return;
        }
        this[a[k]] = this[a[k]] || {};
        if (this[a[k]].data) {
            a[k] = this[a[k]].data;
            return;
        }
        this[a[k]].update = this[a[k]].update || [];
        this[a[k]].update.push(a);
    }, this);
}, Object.create(null));

console.log(data);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • thank a lot... :) I get a random JSON object from the server and the JSON I get from server would have multiple objects referred just like the "agent" object.. I would also like to detect which properties need to be handled so that I don't have to care about the property names for each response from the server... How do I handle the general case? – kukkuz Jul 14 '16 at 12:59
  • you could use the key, which is to set, use as key for `this` object, like `this[key][a[key]] =...`, it depends on the specification, you have. – Nina Scholz Jul 14 '16 at 16:41
  • I am still a novice and bit confused how to do it as per your suggestion- could you please write it down for me? Note that some responses from the server will have multiple properties like "agent", "tenant", "provider", "admin" that will be internally referred and for any particular response I won't be knowing which properties are internally referred. – kukkuz Jul 14 '16 at 17:17
  • the list is known before? – Nina Scholz Jul 14 '16 at 17:20
  • nope, you won't know this list before- it changes on each response. – kukkuz Jul 14 '16 at 17:22
  • how would you decide if the property is relevant or not? – Nina Scholz Jul 14 '16 at 17:24
  • if the property is an object, there is every possibility that there is an internal reference in the JSON – kukkuz Jul 14 '16 at 17:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/117351/discussion-between-nina-scholz-and-kukkuz). – Nina Scholz Jul 14 '16 at 17:36
  • Thanks a lot, this was the solution I was searching for... :) – kukkuz Jul 15 '16 at 01:12
0

I think the only way is to run through the array two times:

UPD:

var arr = [ ... ]; // your list of data
var collectionFields = ['agent', 'someOtherField'];
var collections = {};
// collect all information about agents
for (var i = 0; i < arr.length; i++) {
  var item = arr[i];
  for (var k = 0; k < collectionFields.length; k++) {
    var field = collectionFields[k];
    if (typeof collections[field] === 'undefined') {
      collections[field] = {};
    }
    if (typeof item[field] === 'object') {
      collections[field][item[field].id] = item[field];
    }
  }
}

for (var j = 0; j < arr.length; j++) {
  for (var k = 0; k < collectionFields.length; k++) {
    var field = collectionFields[k];
    if (typeof arr[j][field] === 'number') {
      arr[j][field] = collections[field][arr[j][field]];
    }
}

console.log(arr);
steppefox
  • 1,784
  • 2
  • 14
  • 19
  • i thought about that... The JSON I get from server would have multiple objects referred just like the "agent" object – kukkuz Jul 14 '16 at 11:30
  • if it helps you, you can mark my question as right answer) – steppefox Jul 14 '16 at 17:37
  • still sitting on the problem... Note that for any particular response I won't be knowing which properties are internally referred- I do not know them beforehand – kukkuz Jul 14 '16 at 17:45
0

try this

var data  = [
  { "id": 1,
    "agent": {
      "id": 1,
      "firstName": "gghg",
      "lastName": "gh",
      "phone": "4543534",
      "admin": true
    },
    "user":"agent@gmail.com"
  },
  { "id": 2,
    "agent": 1, // here I want the full object and not the Id
    "user":"agent1@gmail.com"
  }
];
var map = {};
//create a map of items by their id
data.forEach( function(obj){ map[ obj.id ] = obj.agent; } );

//iterate the data array and replace agents by their value if their value is a number.
data = data.map( function(obj){
  if ( !isNaN( obj.agent ) )
  {
     obj.agent = JSON.parse( JSON.stringify( map[ obj.agent ] ) );
  }
  return obj;
});

console.log( data );
gurvinder372
  • 66,980
  • 10
  • 72
  • 94
  • I get a random JSON object from the server and the JSON I get from server would have multiple objects referred just like the "agent" object.. How do I handle the general case? – kukkuz Jul 14 '16 at 11:34
  • @kukkuz you need to then put the property name in a variable say `var prop = 'agent'` and edit it by doing `obj[prop] = JSON.parse( JSON.stringify( map[ obj[ prop ] ] ) )` – gurvinder372 Jul 14 '16 at 11:36
  • yup, thanks... In the general case, I would also like to detect which properties need to be handled so that I don't have to care about the property names for each response from the server... – kukkuz Jul 14 '16 at 12:10
  • how would you like to *detect* the property you need to set? – Nina Scholz Jul 14 '16 at 13:09
  • What I mean is that for instance, some responses from the server will have multiple properties like "agent", "tenant", "provider", "admin" that will be internally referred. – kukkuz Jul 14 '16 at 13:26
0
var a = [ { "id": 1, "agent": {"id": 1, "firstName": "gghg", "lastName": "gh", "phone": "4543534", 
                        "admin": true}, "user":"agent@gmail.com"}, 
            { "id": 2, "agent": 1, "user":"agent1@gmail.com"}];

//on = $.parseJSON(a);
console.log(a);
//nsole.log(bson);
var b=[];
var mapID = [];
for(var key in a) {
    console.log(a[key].agent);
            b[key] = a[key];
    if($.isNumeric(a[key].agent)){
            var id = a[key].agent;
        b[key].agent = a[mapID[id]].agent;
    }
    mapID[a[key].id] = key;
}

console.log(b);

check working demo

Asif Rahaman
  • 775
  • 6
  • 10
0

You can simply define a getter for items which has only the agent ID.

var data = [{ "id": 1, "agent": { "id": 1, "firstName": "gghg", "lastName": "gh", "phone": "4543534", "admin": true }, "user": "agent@gmail.com" }, { "id": 2, "agent": 1, "user": "agent1@gmail.com" }];

console.clear();
data.forEach((hash => d => {
  var agent = d.agent;
  if (typeof agent === 'number')
    Object.defineProperty(d, 'agent', {
      get: () => hash[agent]
    });
  else
    hash[agent.id] = agent;
})(Object.create(null)));

console.log(data);
Morteza Tourani
  • 3,506
  • 5
  • 41
  • 48