How does one create, collect and programmatically (one entry per form-element) aggregate objects from related form-elements?

Solution 1:

The most flexible approach does not make any assumptions about a form's inner DOM hierarchy. It instead would focus on the naming schema which all form-element's name attributes have in common, and which is ... "t-<key>-<id>".

Thus an entirely DOM-structure agnostic but partially re-usable approach would be bipartite / two-folded.

  • The first part does introduce the regex upon which any form-element will be identified. This regex is as follows ... /^t-(?<key>\w+)-(?<id>\d+)$/. It features named groups for capturing a name value's key and id part. The actual task ...

    • transforms the form elements collection into an array

    • filters all valid (according to the regex) form elements.

    • reduces the filtered array into a list of dataItems

    • The reducer function gets provided the regex as part of a config object which is passed as initial value to reduce as well.

  • The second task is implemented as generic and re-usable function which creates and stores dataItem objects upon a form element's extracted id as well as this element's extracted key and its value.

function collectDataItemsByElementKeyAndId(collector, elmNode) {
  const { regX, list, map } = collector;
  const { key, id } = regX.exec(elmNode.name)?.groups ?? {};

  let dataItem = map[id];
  if (!dataItem) {

    dataItem = map[id] = {};
    list.push(dataItem);
  }
  Object.assign(dataItem, { [key]: elmNode.value });
  
  return collector;
}

function handleSubmit(evt) {
  evt.preventDefault();

  const regXKeyAndId = /^t-(?<key>\w+)-(?<id>\d+)$/;

  const dataItemList = Array
    .from(this.elements) // `this` refers to the form element.

    .filter(elm => regXKeyAndId.test(elm.name))
    .reduce(collectDataItemsByElementKeyAndId, {

      regX: regXKeyAndId,
      map: {},
      list: [],

    }).list;

  console.log({ dataItemList });
}

$('#myForm').on('submit', handleSubmit);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<form id="myForm">
  <div id="parent-div">
    <div>
      <input type="text" style="margin: 0;" name="t-name-1" value="value1" />
      <span>
        <input type="text" name="t-quantity-1" value="1" />
        <input type="text" name="t-frequency-1" value="6" />
      </span>
    </div>
    <div>
      <input type="text" style="margin: 0;" name="t-name-2" value="value2" />
      <span>
        <input type="text" name="t-quantity-2" value="2" />
        <input type="text" name="t-frequency-2" value="8" />
      </span>
    </div>
  </div>
  <button type="submit">
    Submit
  </button>
</form>

Solution 2:

$('#myForm').on('submit', function(e) {
  e.preventDefault();
  var divs = document.querySelectorAll('#myForm > #parent-div > div')
  var group = []

  divs.forEach(div => {
    var inputs = div.querySelectorAll('input')
    var obj = {}

    inputs.forEach(i => {
      var name = i.getAttribute('name').split('-')[1]
      obj[name] = i.value
    })

    group.push(obj)
  })

  console.log(group)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="myForm">
  <div id="parent-div">
    <div>
      <input type="text" style="margin: 0;" name="t-name-1" value="value1" />
      <span>
        <input type="text" name="t-quantity-1" value="1" />
        <input type="text" name="t-frequency-1" value="6" />
      </span>
    </div>
    <div>
      <input type="text" style="margin: 0;" name="t-name-2" value="value2" />
      <span>
        <input type="text" name="t-quantity-2" value="2" />
        <input type="text" name="t-frequency-2" value="8" />
      </span>
    </div>
  </div>
  <button type="submit">
    Submit
  </button>
</form>