Skip to content
Advertisement

Convert nested array of array to nested array of object

I have currently the challenge to convert the following data:

{
  "fieldname1": "value",
  "fieldname3": [
    "value 1",
    [
      [
        "firstname",
        "lastname"
      ]
    ]
  ],

  "fieldname2": [
    [
      [
        "value 3"
      ],
      "full name"
      
    ] 
  ]
}

into the following:

{
  "fieldname1": "value",
  "fieldname3": {
    "field3_sub1": "value 1",
    "field3_sub2": [
      {
        "field3_sub2_1": "firstname",
        "field3_sub2_2": "lastname"
      }
    ]
  },

  "fieldname2": [
    {
      "field2_sub1": [
        "value 3"
      ],
      "field2_sub2": "full name"
      
    } 
  ]
}

I’m currently a bit lost how I could convert the source data into the expected object.

I get the data from a sql query ( presto ).

The structured information ( fieldname2 and fieldname3 ) aren’t resolved, I get only the values in the response.

The field position at root level is dynamic ( could be fieldname1, fieldname2, fieldname3 but also fieldname3, fieldname1, fieldname2).

The value position inside fieldname2 and fieldname3 seems to be static.


I already solved the challenge to convert the given table schema into a JSONSchema:

{
  "type": "object",
  "properties": {
    
    "fieldname2": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "field2_sub1": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "field2_sub2": {
            "type": "string"
          }
          
        },
        "additionalProperties": false,
        "title": "fieldname2"
      }
    },
    "fieldname1": {
      "type": "string"
    },
    
    "fieldname3": {
      "type": "object",
      "properties": {
        "field3_sub1": {
          "type": "string"
        },
        "field3_sub2": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "field3_sub2_1": {
                "type": "string"
              },
              "field3_sub2_2": {
                "type": "string"
              },
              
            },
            "additionalProperties": false,
            "title": "field3_sub2"
          }
        }
      },
      "additionalProperties": false,
      "title": "fieldname3"
    }
  },
  "additionalProperties": false
}

Based on the JSONSchema I was able to generate a “model definition” which will be used in a code generator:

{
  "Fieldname2": {
    "root": false,
    "fields": {
      "field2_sub1": {
        "name": "field2_sub1",
        "fieldType": "string[]",
        "graphqlType": "[String]",
        "nullable": true,
        "filter": false
      },
      "field2_sub2": {
        "name": "field2_sub2",
        "fieldType": "string",
        "graphqlType": "String",
        "nullable": true,
        "filter": true
      }
    }
  },
  "Field3_sub2": {
    "root": false,
    "fields": {
      "field3_sub2_1": {
        "name": "field3_sub2_1",
        "fieldType": "string",
        "graphqlType": "String",
        "nullable": true,
        "filter": true
      },
      "field3_sub2_2": {
        "name": "field3_sub2_2",
        "fieldType": "string",
        "graphqlType": "String",
        "nullable": true,
        "filter": true
      }
    }
  },
  "Fieldname3": {
    "root": false,
    "fields": {
      "field3_sub1": {
        "name": "field3_sub1",
        "fieldType": "string",
        "graphqlType": "String",
        "nullable": true,
        "filter": true
      },
      "field3_sub2": {
        "name": "field3_sub2",
        "fieldType": "Field3_sub2[]",
        "graphqlType": "[Field3_sub2]",
        "nullable": true,
        "filter": false
      }
    }
  },
  "Record": {
    "root": true,
    "fields": {
      "fieldname2": {
        "name": "fieldname2",
        "fieldType": "Fieldname2[]",
        "graphqlType": "[Fieldname2]",
        "nullable": true,
        "filter": false
      },
      "fieldname1": {
        "name": "fieldname1",
        "fieldType": "string",
        "graphqlType": "String",
        "nullable": true,
        "filter": true
      },
      "fieldname3": {
        "name": "fieldname3",
        "fieldType": "Fieldname3",
        "graphqlType": "Fieldname3",
        "nullable": true,
        "filter": false
      }
    }
  }
}

Advertisement

Answer

Based on the comment from @nikhil, I spent an hour to create a running prototype.

I have tested it successfully with the provided example data and real data.

It worked in both cases.

const transformObject = (data, definition) => {
  let res = {};
  
  // get the field names from the definition
  const definitionFields = Object.keys(definition);

  for (let iElement = 0; iElement < data.length; iElement++) {
    // get the current property name  
    const propName = definitionFields[ iElement ]
    // get the definition for the current field
    const fieldDefinition = definition[ propName ];

    // in case the current value is a simple type, return it
    if (fieldDefinition.type !== "array" && fieldDefinition.type !== "object") {
      res[ propName ] = data[ iElement ];
    }

    // if the expected value for the current property is an object,
    // run the object transformation
    if (fieldDefinition.type === "object") {
      res[ propName ] = transformObject(
        data[ iElement ],
        fieldDefinition.properties
      );
    }

    // if the expected value for the current property is an array,
    // run the array transformation
    if (fieldDefinition.type === "array") {
      res[ propName ] = transformArray(
        data[ iElement ],
        fieldDefinition.items
      );
    }
  }
  return res;
};

const transformArray = (data, definition) => {
  // in case we have a simple type like string, number, etc.
  // use the given data as return value
  if (definition.type !== "object") {
    return data;
  }

  // otherwise run the transform object function
  // to generate the key/value pair
  // NOTE: Since I don't have array of arrays in my usecase
  // I didn't implemented the check for arrays
  // i assume that the return value should be always
  // an array of object or array of string/number/boolean
  return data.map((ele) => {
    return transformObject(ele, definition.properties);
  });
};

const transform = (data, definition) => {
  let res = {};

  for (const [ key, value ] of Object.entries(data)) {
    // get the current field definition from the schema
    const fieldDefinition = definition[ key ];

    // for simple types like string, number, etc. we don't have
    // to run a transformation, just return the given value
    if (fieldDefinition.type !== "object" && fieldDefinition.type !== "array") {
      res[ key ] = value;
    }

    // in case of an object, run the object transformation
    if (fieldDefinition.type === "object") {
      res[ key ] = transformObject(value, fieldDefinition.properties);
    }

    // in case of an array, run the array transformation
    if (fieldDefinition.type === "array") {
      res[ key ] = transformArray(value, fieldDefinition.items);
    }
  }

  return res;
};

const source = {
  /**
   * The provided data from the question
   */
}

const schema = {
  /**
   * the provided JSONSchema from the question
   */
}
const transformed = transform(source, schema.properties);
console.log(JSON.stringify(transformed, null, 2))
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement