Home > Back-end >  Joining array of objects keys with "same" name into one array
Joining array of objects keys with "same" name into one array

Time:05-03

I'm attempting to join an array of objects with the "same" keys as one array. Basically the what I mean by "same keys" is that these keys will vary in only on number, in which doesn't matter. For example:

{ names.0.id: "Here" }
{ names.1.id: "Almost" }
{ names.2.id: "There" }
...

I wan't to join all the keys that have the following syntax into one. Like:

{ names: ["Here", "Almost", "There"] }

I have a array of arrays containing this data.

[{
  "id": "123"
}, {
  "group_name": "Test Group"
}, {
  "admin": "Somthing"
}, {
  "email_address": "[email protected]"
}, {
  "org.id": "4"
}, {
  "created_by": "6"
}, {
  "updated_by": "6"
}, {
  "students.0.id": "Yes"
}, {
  "students.1.id": "No"
}, {
  "names.0.id": "Here"
}, {
  "names.1.id": "Almost"
}, {
  "names.2.id": "There"
}],
[{
  "id": "125"
}...
]
...

What I would like to accomplish:

[{
  "id": "123"
}, {
  "group_name": "Test Group"
}, {
  "admin": "Somthing"
}, {
  "email_address": "[email protected]"
}, {
  "org.id": "4"
}, {
  "created_by": "6"
}, {
  "updated_by": "6"
}, {
  "students": ["Yes", "No"]
}, {
  "names": ["Here", "Almost", "There"]
}]

For any other keys that have this syntax of:

name.number.id

I would always like to join in a single array with the first part of the name

CodePudding user response:

I would suggest to split the problem in to three parts:

  1. Transform your arrays of { key: value } objects in to single, nested objects
  2. Flatten such nested objects to replace the { id: ... } objects with just their values
  3. Translate back to one-object per key-value-pair

1. Merging to a single object

To build up a nested object, we split each key by ".". We then loop over the keys in pairs to create our nested layers. When a key is numeric, we add an array. See mergeKvp in the snippet below.

2. Flattening to get rid of { id }

To flatten, we traverse the tree. Whenever we see an object that has only an id property, we replace it by that object's id value. See flattenObj in the snippet below.

//// UTILS
// Merge a key value pair in to an object splitting by "."
const mergeKvp = (obj, [key, value]) => {
  const path = key.split(".");
  
  let target = obj;
  for (let i = 0; i < path.length - 1; i  = 1) {
    const k1 = path[i];
    const k2 = path[i   1];
    
    if (!target[k1]) target[k1] = isNaN( k2) ? {} : [];
    
    target = target[k1];
  }
  
  target[path.at(-1)] = value;
  
  return obj;
}

// Flatten a nested object structure
const flattenObj = obj => {
  // Handle arrays
  if (Array.isArray(obj)) return obj.map(flattenObj);
  
  // Handle objects
  if (Object(obj) === obj) {
    // Replace { id: _ } objects with _
    if ("id" in obj && Object.keys(obj).length === 1) return obj.id;
    
    // Recurse for other objects
    return Object.fromEntries(
      Object.entries(obj).map(([k, v]) => [k, flattenObj(v)])
    );
  }
  
  // Return value for everything else
  return obj;
}

//// APP
// Merge KVP objects in to one big object
const base = Object.assign({}, ...getData());

// Create nested structures out of the dot-keys
const tree = Object.entries(base).reduce(mergeKvp, {})

// Flatten the tree
const output = flattenObj(tree);

// Translate back to array of { key: value } objects
console.log(
  Object.entries(output).map(([k, v]) => ({ [k]: v }))
);


function getData() { return [{
  "id": "123"
}, {
  "group_name": "Test Group"
}, {
  "admin": "Somthing"
}, {
  "email_address": "[email protected]"
}, {
  "org.id": "4"
}, {
  "created_by": "6"
}, {
  "updated_by": "6"
}, {
  "students.0.id": "Yes"
}, {
  "students.1.id": "No"
}, {
  "names.0.id": "Here"
}, {
  "names.1.id": "Almost"
}, {
  "names.2.id": "There"
}]; };

  • Related