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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.