Search code examples
javascriptarraysformsform-data

Structure formData in an array


I have an extensive form and I'm trying to organize this so that it remains practicable for the plan that I intend to have with it.

An example showing what my form looks like:

<input type="text" name="data[0][name]">
<input type="email" name="data[0][email]">
<input type="tel" name="data[0][phone]">

<input type="text" name="data[1][name]">
<input type="email" name="data[1][email]">
<input type="tel" name="data[1][phone]">

I want this to return as:

 - data
    - data[0]
       - data[name]
       - data[email]
       - data[phone]
    - data[1]
       - data[name]
       - data[email]
       - data[phone]

But with new FormData(form); and then Object.fromEntries(formData) I'm just getting it on separate lines (as one large array).

Can someone help me with an easy way of converting these in a dynamic way because in my actual setup I have dozens of fields. So manually is not a desired option for me.

Thanks!


Solution

  • To convert a (single layer) NodeList of form elements into a nested object, using the form element name components, you can loop over the data form elements:

    const dataform_obj = {} // instantiate empty object
    
    // specify regular expression to capture name components
    // for example: data[0][email]
    const dataform_re = /^([^\[]+)(\[\d+\])(\[[^\]]+\])$/;
    
    // convert form elements into array then loop
    [...document.querySelector('#dataform').elements].forEach(function (elem) {
    
        // capture name components
        const match = elem.name.match(dataform_re);
        // match[1] is first name, match[2] is index, match[3] is last name
        // for example, ["data", "0", "email"]
    
      // instantiate sub-objects if do not exist
      if ( !dataform_obj[match[1]] ) { 
        dataform_obj[match[1]] = {}; 
      }
      if ( !dataform_obj[match[1]][match[1] + match[2]] ) { 
        dataform_obj[match[1]][match[1] + match[2]] = {}; 
      }
    
      // assign element value to object
      dataform_obj[match[1]][match[1] + match[2]][match[1] + match[3]] = elem.value;
    })
    

    Above, a javascript regular expression is used to capture the various components of the form element name. Those components are then used to build the object hierarchy.

    console.log(dataform_obj);
    

    ...produces:

    data: {
      data[0]: {
        data[email]: "",
        data[name]: "",
        data[phone]: ""
      },
      data[1]: {
        data[email]: "",
        data[name]: "",
        data[phone]: ""
      }
    

    Online example: https://jsfiddle.net/n4m32wzh/


    Perhaps, this is the format you're actually looking for:

    data: {
      0: {
        email: "",
        name: "",
        phone: ""
      },
      1: {
        email: "",
        name: "",
        phone: ""
      }
    }
    

    Do that like this:

    const dataform_obj = {};
    const dataform_re = /^([^\[]+)\[(\d+)\]\[([^\]]+)\]$/;
    
    [...document.querySelector('#dataform').elements].forEach(function (elem) {
    
      const match = elem.name.match(dataform_re)
    
      if ( !dataform_obj[match[1]] ) { 
        dataform_obj[match[1]] = {}; // <-- this level can alternatively be an array using [] instead of {} since the component is a number
      }
      if ( !dataform_obj[match[1]][match[2]] ) { 
        dataform_obj[match[1]][match[2]] = {}; 
      }
    
      dataform_obj[match[1]][match[2]][match[3]] = elem.value;
    });
    console.log(dataform_obj["data"])
    

    Online example: https://jsfiddle.net/gnav6qb4/2/


    If you already have a FormData Object, you can loop this way:

    const dataform = new FormData(document.querySelector('#dataform'));
    [...dataform.entries()].forEach(function (elem) {
      // elem is an array: [element name, element value]
      //...