0

I know the title might be a bit confusing so I'll try to explain visually. I've been struggling for days, I've searched all over but this is a very specific task.

I'm being provided with this API response:

{
  akash: {
    url: "127.0.0.1",
    count: 12
  },
  aptos: {
    url: "127.0.0.2",
    count: 54
  }
}

I need to convert it into a new object with this format:

{
  akash: {
    name: "akash",
    url: "127.0.0.1",
    count: 12
  },
  aptos: {
    name: "aptos",
    url: "127.0.0.2",
    count: 54
  }
}

If you can tell, now every subset has a new property "name" with the parent object's key name as its value.

Another alternative would be to convert it into a proper array of objects, like this:

[{
  name: "akash",
  url: "127.0.0.1",
  count: 12
}, {
  name: "aptos",
  url: "127.0.0.2",
  count: 54
}]
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37

5 Answers5

0

You can iterate through the keys of the object.

Given you have an originalObject as your starting point, you can create a new object using the following code:

const newObject = {};
for (const key in originalObject) {
    if (originalObject.hasOwnProperty(key)) {
        const value = { ...originalObject[key], name: key };
        newObject[key] = value;
    }
}

To create a list, the process is quite similar, but you're generating a new array of objects and pushing the new values to the list:

const newList = [];
for (const key in originalObject) {
    if (originalObject.hasOwnProperty(key)) {
        const value = { ...originalObject[key], name: key };
        newList.push(value);
    }
}
  • 1
    [Don't use `originalObject.hasOwnProperty(key)`](https://stackoverflow.com/q/45014347/1048572). Otherwise, this answer seems fine – Bergi Aug 23 '23 at 04:57
0

https://tsplay.dev/WPB8ZN

type AddName<O, P = never> = {
  [K in keyof O | (P extends never ? never : 'name')]: K extends keyof O ?
  O[K] extends object ? AddName<O[K], K> : O[K]
  : P
} & unknown

function addName<O extends object, S>(o: O, name?: S): AddName<O, unknown extends S ? never : S> {
  if (name !== undefined) Object.assign(o, {name})
  for (let [k, v] of Object.entries(o)) {
    if (typeof o === 'object'&& o !== null) {
      addName(v, k)
    }
  }
  return o as any;
}

This does work recursively (note this adds name to existing objects, not creates new ones)

Dimava
  • 7,654
  • 1
  • 9
  • 24
0

When working with JSON, the objects can be modified directly when deserialising by using the the reviver parameter of JSON.parse().

function reviver(key, value) {
  const isObject = typeof value === "object" && value !== null && !Array.isArray(value);
  const isInsideArray = Array.isArray(this);
  
  if (key !== "" && isObject && !isInsideArray) {
    value.name = key;
  }
  
  return value;
}

const json = `{
   "akash": {
      "url": "127.0.0.1",
      "count": 12
   },
   "aptos": {
      "url": "127.0.0.2",
      "count": 54
   }
}`;

const obj = JSON.parse(json, reviver);

console.log(obj);
.as-console-wrapper { max-height: 100% !important; }

This reviver will assign name property to any object to be the same as the key that contains it. It skips doing that for arrays or items contained within to avoid adding names as indexes:

function reviver(key, value) {
  const isObject = typeof value === "object" && value !== null && !Array.isArray(value);
  const isInsideArray = Array.isArray(this);
  
  if (key !== "" && isObject && !isInsideArray) {
    value.name = key;
  }
  
  return value;
}

const json = `{
   "akash": {
      "url": "127.0.0.1",
      "count": 12
   },
   "aptos": {
      "url": "127.0.0.2",
      "count": 54
   },
   "array": [{"foo": 1}, {"foo": 2}, {"foo": 3}]
}`;

const obj = JSON.parse(json, reviver);

console.log(obj);
.as-console-wrapper { max-height: 100% !important; }

If instead a name should be added to objects in an array, then this example shows how to do it. Here all objects will get the key that holds the array value:

function reviver(key, value) {
  const isObject = typeof value === "object" && value !== null;
  const isInsideArray = Array.isArray(this);
  
  if (key !== "" && isObject && !isInsideArray) {
    if (Array.isArray(value))
      return value.map(x => reviver(key, x, true));
    else 
      value.name = key;
  }
  
  return value;
}

const json = `{
   "akash": {
      "url": "127.0.0.1",
      "count": 12
   },
   "aptos": {
      "url": "127.0.0.2",
      "count": 54
   },
   "array": [{"foo": 1}, {"foo": 2}, {"foo": 3}]
}`;

const obj = JSON.parse(json, reviver);

console.log(obj);
.as-console-wrapper { max-height: 100% !important; }
VLAZ
  • 26,331
  • 9
  • 49
  • 67
0

Since the OP wants to alter the data-structure of object-based data, the main task always has to iterate over all of an object's entries, regardless of the final base data structure which either is an array or an object again.

A method like Object.entries provides an array of all of an object's entries.

In case of an array as target data-structure one just needs to map each entry which is an array/tuple of a single key and its related value. The entry specific new object then could be generated via an object literal and spread syntax.

In case of an object as target data-structure one could reduce the entries-array into an object again by passing an empty object (e.g. an empty object literal) as initial value to the reduce method's 2nd parameter. The final result then gets aggregated stepwise with each iteration via Object.assign where one does merge a newly created, entry specific object into the current object.

const sampleData = {
  akash: {
    url: "127.0.0.1",
    count: 12
  },
  aptos: {
    url: "127.0.0.2",
    count: 54
  }
};

const arrayOfMappedDataEntries = Object
  .entries(sampleData)
  .map(([name, data]) => ({ name, ...data }));

const alteredObjectStructure = Object
  .entries(sampleData)
  .reduce((result, [name, data]) =>
    Object.assign(result, { [ name ]: { name, ...data } }), {}
  );

console.log({
  sampleData,
  alteredObjectStructure,
  arrayOfMappedDataEntries,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

Edit ... due to Bergi's comment ...

"Ah yet another take at reduce, with Object.assign this time… Why not use Object.fromEntries?" – Bergi

const sampleData = {
  akash: {
    url: "127.0.0.1",
    count: 12
  },
  aptos: {
    url: "127.0.0.2",
    count: 54
  }
};

const alteredObjectStructure = Object
  .fromEntries(
    Object
      .entries(sampleData)
      .map(([name, data]) => [name, { name, ...data }])
  );

console.log({
  sampleData,
  alteredObjectStructure,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • 1
    Ah yet another take at `reduce`, with `Object.assign` this time… Why not use `Object.fromEntries`? – Bergi Aug 23 '23 at 11:04
  • @Bergi ... I know, from my answers one could assume I'm obsessed with `reduce`. But here it is the more direct way for altering an object into another object. Of cause one could do `Object.fromEntries(Object.entries(...).map(...))` where `fromEntries` is an additionally iterating task over `entries` and `map`. And mentioning/proposing `reduce` a lot is mainly for making people aware of this very versatile/mighty method (it's the MacGyver array tool). – Peter Seliger Aug 23 '23 at 11:21
-1

I'm a beginner in JavaScript. If I were to write that code, I think I would write it like this


const source = {
   "akash": {
      "url": "127.0.0.1",
      "count": 12
   },
   "aptos": {
      "url": "127.0.0.2",
      "count": 54
   }
};

const result = {...source}

for (const key in source) {
 result[key].name = key;
}



jay
  • 1
  • `{...source}` does not create a deep copy. The objects you are modifying are part of both `source` and `result`. – Bergi Aug 23 '23 at 09:19
  • @Bergi you're right. They share the reference. I never thought about deep copy. Thank you for letting me know. I'm going to figure it out. – jay Aug 23 '23 at 13:22
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 25 '23 at 10:46