0

Let's say I have a form, and I want to check what inputs has been declared in the form, so I do something like this:

let inputs = {};

['first_name', 'last_name', ...].forEach(name => {
    let input = document.querySelector(`input[name="${name}"]`);

    if (input !== undefined)
        inputs = Object.assign(inputs, {[input.getAttribute('name')]: input.value});
}

In this case, so far so good.

But what if have some inputs called ['name.first', 'name.last']? then, it will store the data to an object like this:

{name.first: '...', name.last: '...'}.

and not like this: (which is my desired result)

{
    name: {
        first: '...',
        last: '...'
    }
}

How can I tell JavaScript to parse it as an object path instead of a key string?

2
  • Your first example would be better written with forEach or even reduce as you are not actually performing a map. I'm also curious as to why you use a fixed array of names to filter results? Perhaps you could explain a little more about what you are doing? Commented Dec 18, 2016 at 11:18
  • you were right, using map instead of forEach wasn't smart at all. It was a mistake. Commented Dec 18, 2016 at 11:34

3 Answers 3

1

I would use a custom assigning function.

An object can use any string as key, so "a.b" is valid.

Here is an example of an assign function:

function assign(obj, key, value) {
    let keys = key.split(".");
    let last = keys.pop();
    keys.forEach(key => {
        if(!obj[key])
            obj[key] = {};
        obj = obj[key];
    });

    obj[last] = value;
}

let obj = {};
assign(obj, "name.first", "hey");
assign(obj, "name.last", "bye");

console.log(obj);

Bonus recomendation: you can use it as a global form handler, if you use the same format to all forms.

function formHandler(form) {
    let obj = {};
    form.querySelectorAll("input, select").forEach(elem => assign(obj, elem.name, elem.value));
    return obj;
}
Sign up to request clarification or add additional context in comments.

Comments

0

Something like that?

  var obj = {
      'prop': 'value',
      'nested.prop1': 'value1',
      'nested.prop2': 'value2',
      'renested.prop1': 'value1',
      'renested.prop2': 'value2',    
      'nested.prop3': 'value3',
  }

  function deconcatProp(obj){

      var idx;
      var key;

      for(var prop in obj){
          if((idx = prop.indexOf('.')) > 0){
              key = prop.substring(0, idx);
              if(!obj[key]) {
                  obj[key] = {
                  };
              }
              obj[key][prop.substring(idx + 1, prop.length)] = obj[prop];
              delete obj[prop];
          }
      }

  }

 deconcatProp(obj);

Comments

0

Using the same principle as the other answers (split, test and create) for creating paths, but demonstrates an alternative of your filtering example (as mentioned in my comment).

const wanted = ['name.first', 'name.second', 'name.last'];
const result = Array.from(document.querySelectorAll('input'))
  .filter(input => wanted.includes(input.name))
  .reduce((a, input) => {
    const paths = input.name.split('.');
    const key = paths.pop();
    let current = a;
    paths.forEach(path => current = current[path] ?
      current[path] :
      current[path] = Object.create(null));
    current[key] = input.value;
    return a;
  }, Object.create(null));

console.log(result);
<input name="name.first" value="Eliya" />
<input name="name.middle" value="Wibble" />
<input name="name.last" value="Cohen" />

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.