285

I have a string as

string = "firstName:name1, lastName:last1"; 

now I need one object obj such that

obj = {firstName:name1, lastName:last1}

How can I do this in JS?

4
  • 2
    Are name1 and last1 string values or identifiers that have been defined elsewhere? Commented Jul 6, 2009 at 10:52
  • 1
    More data needed... what are you doing to do if the key or the value contain a comma or a colon? Commented May 15, 2017 at 20:58
  • If your string is not format as JSON, you may have to use RegEp to handler. Commented Nov 13, 2018 at 13:25
  • Actually your string must be in JSON format for easy conversion Commented Oct 14 at 5:20

21 Answers 21

234

Actually, the best solution is using JSON:

Documentation

JSON.parse(text[, reviver]);

Examples:

1)

var myobj = JSON.parse('{ "hello":"world" }');
alert(myobj.hello); // 'world'

2)

var myobj = JSON.parse(JSON.stringify({
    hello: "world"
});
alert(myobj.hello); // 'world'

3) Passing a function to JSON

var obj = {
    hello: "World",
    sayHello: (function() {
        console.log("I say Hello!");
    }).toString()
};
var myobj = JSON.parse(JSON.stringify(obj));
myobj.sayHello = new Function("return ("+myobj.sayHello+")")();
myobj.sayHello();
Sign up to request clarification or add additional context in comments.

12 Comments

That is true, however strictly speaking functions should not be in any JSON objects.. For that you have RPC etc, or if you want, you can pass the prototype of a function to the json, and do eval later.
Doesn't answer the question at all I'm afraid. It would be wonderful if everyone changed the question to suit the answer. How do you parse "firstName:name1, lastName:last1"? not '{ "hello":"world" }'.
This doesn't answer the question, I don't know why it has so many upvotes. The questioner (and myself, I googled and ended up here) have data that doesn't conform to JSON standard so can't be parsed.
var a = "firstName:name1, lastName:last1"; JSON.parse('{' + a + '}') throws an error.
@TravisJ Your comment is just as wrong as this answer. For JSON.parse to work, you'd need to be parsing the input '{"firstName":"name1", "lastName":"last1"}'. Note all the additional quotes.
|
95

Your string looks like a JSON string without the curly braces.

This should work then:

obj = eval('({' + str + '})');

WARNING: this introduces significant security holes such as XSS with untrusted data (data that is entered by the users of your application.)

19 Comments

this is a potential security hole. I wouldn't reccomend this method at all.
It depends where the data comes from. Some people are obsessed with security. If you control your data, you can be more pragmatic in finding solutions. Or do you have a burglar-proof door between your kitchen and living room?
That's not working. It gives you error 'SyntaxError: Unexpected token :'. I've checked it in the Chrome.
The solution needs parentheses around it: obj = eval('({' + str + '})');
prc322: Everyone knows that eval() is not very secure (and that's an understatement), but using eval() is the only correct answer to the question above because the input string is not a valid JSON string AND the input string references other variables by name.
|
69

If I'm understanding correctly:

var properties = string.split(', ');
var obj = {};
properties.forEach(function(property) {
    var tup = property.split(':');
    obj[tup[0]] = tup[1];
});

I'm assuming that the property name is to the left of the colon and the string value that it takes on is to the right.

Note that Array.forEach is JavaScript 1.6 -- you may want to use a toolkit for maximum compatibility.

6 Comments

Hey Cdleary... i wonder if you can help me: stackoverflow.com/questions/9357320/… Sorry couldnt find another way to contact you except thru comments on ur answers
Prefect post. This answer uses the basics of JavaScript very clear and should thereby works in every browser
I found this approach was a good start, so the answer was useful. However, xecute has a valid point that it doesn't accommodate strings that may have commas. You could go down the path of matching quotes if your code needs to handle strings with commas, but you still wont catch cases where the quotes are escaped. Just implement what you need, then stop.
In a question of parsing text presented by the asker, questions of "what if" are useless. We can accept the given example as representative of the data or we can simply not answer since there could always be a "what if" that will break the parsing.
The poster asked about how to parse an unusual structure that was not valid JSON. @cdleary answered the question very well under the circumstances. xecute: handling a comma in a string would require a quoting or escaping mechanism, beyond the scope of this discussion.
|
51

This simple way...

var string = "{firstName:'name1', lastName:'last1'}";
eval('var obj='+string);
alert(obj.firstName);

output

name1

3 Comments

Upvote for this, ALTHOUGH: var string = "{firstName:'name1', lastName:'last1', f: function(){alert('u got hacked...')}()}"; eval('var obj'+string) This could get you hacked... but I think its worth while because its the only thing that works in some cases.
This was the only solution that could directly solve the question, although using eval is dangerous.
Currently, any JS code executed in the browser is dangerous. Let's be frank... This answer is the only solution that could effectively solve the question "string to object in JS". JSON.parse() is limited to the scope of JSON. Remembering that a JSON is not a JS Object. Thanks! =D
32

Since JSON.parse() method requires the Object keys to be enclosed within quotes for it to work correctly, we would first have to convert the string into a JSON formatted string before calling JSON.parse() method.

var obj = '{ firstName:"John", lastName:"Doe" }';

var jsonStr = obj.replace(/(\w+:)|(\w+ :)/g, function(matchedStr) {
  return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":';
});

obj = JSON.parse(jsonStr); //converts to a regular object

console.log(obj.firstName); // expected output: John
console.log(obj.lastName); // expected output: Doe

This would work even if the string has a complex object (like the following) and it would still convert correctly. Just make sure that the string itself is enclosed within single quotes.

var strObj = '{ name:"John Doe", age:33, favorites:{ sports:["hoops", "baseball"], movies:["star wars", "taxi driver"]  }}';

var jsonStr = strObj.replace(/(\w+:)|(\w+ :)/g, function(s) {
  return '"' + s.substring(0, s.length-1) + '":';
});

var obj = JSON.parse(jsonStr);
console.log(obj.favorites.movies[0]); // expected output: star wars

2 Comments

This is the closest solution without using eval() and almost works with all JS Objects in String. But beware, that this solution is not working with timestamps as values! i.e. {from:"2021-05-09T22:00:00.000Z"} is probably converted as {"from":"2021-05-09T"22":"00":00.000Z"}
Your quoting is different than the question.
18

If you have a string like foo: 1, bar: 2 you can convert it to a valid obj with:

str
  .split(',')
  .map(x => x.split(':').map(y => y.trim()))
  .reduce((a, x) => {
    a[x[0]] = x[1];
    return a;
  }, {});

Thanks to niggler in #javascript for that.

Update with explanations:

const obj = 'foo: 1, bar: 2'
  .split(',') // split into ['foo: 1', 'bar: 2']
  .map(keyVal => { // go over each keyVal value in that array
    return keyVal
      .split(':') // split into ['foo', '1'] and on the next loop ['bar', '2']
      .map(_ => _.trim()) // loop over each value in each array and make sure it doesn't have trailing whitespace, the _ is irrelavent because i'm too lazy to think of a good var name for this
  })
  .reduce((accumulator, currentValue) => { // reduce() takes a func and a beginning object, we're making a fresh object
    accumulator[currentValue[0]] = currentValue[1]
    // accumulator starts at the beginning obj, in our case {}, and "accumulates" values to it
    // since reduce() works like map() in the sense it iterates over an array, and it can be chained upon things like map(),
    // first time through it would say "okay accumulator, accumulate currentValue[0] (which is 'foo') = currentValue[1] (which is '1')
    // so first time reduce runs, it starts with empty object {} and assigns {foo: '1'} to it
    // second time through, it "accumulates" {bar: '2'} to it. so now we have {foo: '1', bar: '2'}
    return accumulator
  }, {}) // when there are no more things in the array to iterate over, it returns the accumulated stuff

console.log(obj)

Confusing MDN docs:

Demo: http://jsbin.com/hiduhijevu/edit?js,console

Function:

const str2obj = str => {
  return str
    .split(',')
    .map(keyVal => {
      return keyVal
        .split(':')
        .map(_ => _.trim())
    })
    .reduce((accumulator, currentValue) => {
      accumulator[currentValue[0]] = currentValue[1]
      return accumulator
    }, {})
}

console.log(str2obj('foo: 1, bar: 2')) // see? works!

5 Comments

This is a totally incomprehensible answer for the average reader. Can you explain what each lines does? And what is the resulting output, given OP's input?
Fair enough. Typically I don't have time to detail stuff and I'm just dropping helpful bits (specifically in case I run into the same exact SO question down the road), but I gotchu.
This is definitely the most elegant solution and actually answers the question unlike a lot of the other answers. And thank you for the explanation!
This will only work for simple object values. If we enter things like "google.com" it will break.
Does not work for nested objects. Try this: str2obj( 'x:10, y: {z: 10}' )
13

You need use JSON.parse() for convert String into a Object:

var obj = JSON.parse('{ "firstName":"name1", "lastName": "last1" }');

1 Comment

Your quoting is different than the question.
11

if you're using JQuery:

var obj = jQuery.parseJSON('{"path":"/img/filename.jpg"}');
console.log(obj.path); // will print /img/filename.jpg

REMEMBER: eval is evil! :D

3 Comments

jQuery uses eval. globalEval: /** code **/ window[ "eval" ].call( window, data ); /** more code **/
The string, provided by question owner is not a valid JSON string. So, this code is useless...
yes, eval is not evil if you use it on a "controlled" environment (anything but external data like files, network or user input, in that cases it could be dangerous if not "filtered/validated").
11

In your case, The short and beautiful code

Object.fromEntries(str.split(',').map(i => i.split(':')));

5 Comments

This would work for a flat object but does not work for more complex cases with sub objects.
awesome answer, deserves more votes
should be answer
To strip whitespace: Object.fromEntries(str.split(',').map(i => i.split(':').map(x => x.trim()));
Note property values cannot contain ',' or ':', even escaped
5

I implemented a solution in a few lines of code which works quite reliably.

Having an HTML element like this where I want to pass custom options:

<div class="my-element"
    data-options="background-color: #dadada; custom-key: custom-value;">
</div>

a function parses the custom options and return an object to use that somewhere:

function readCustomOptions($elem){
    var i, len, option, options, optionsObject = {};

    options = $elem.data('options');
    options = (options || '').replace(/\s/g,'').split(';');
    for (i = 0, len = options.length - 1; i < len; i++){
        option = options[i].split(':');
        optionsObject[option[0]] = option[1];
    }
    return optionsObject;
}

console.log(readCustomOptions($('.my-element')));

1 Comment

+1 for using the data- attribute instead of creating pseudo-custom attributes like some frameworks/libraries.
5

I'm using JSON5, and it's works pretty well.

The good part is it contains no eval and no new Function, very safe to use.

1 Comment

no example given
3
string = "firstName:name1, lastName:last1";

This will work:

var fields = string.split(', '),
    fieldObject = {};

if( typeof fields === 'object') ){
   fields.each(function(field) {
      var c = property.split(':');
      fieldObject[c[0]] = c[1];
   });
}

However it's not efficient. What happens when you have something like this:

string = "firstName:name1, lastName:last1, profileUrl:http://localhost/site/profile/1";

split() will split 'http'. So i suggest you use a special delimiter like pipe

 string = "firstName|name1, lastName|last1";


   var fields = string.split(', '),
        fieldObject = {};

    if( typeof fields === 'object') ){
       fields.each(function(field) {
          var c = property.split('|');
          fieldObject[c[0]] = c[1];
       });
    }

2 Comments

Just an idea - instead of second split(':') you could split the string "manually" by FIRST colon using indexOf(":") and consider the part of the string until this colon as key and the rest after the colon as the value.
http doesn't appear in the question.
2

This is universal code , no matter how your input is long but in same schema if there is : separator :)

var string = "firstName:name1, lastName:last1"; 
var pass = string.replace(',',':');
var arr = pass.split(':');
var empty = {};
arr.forEach(function(el,i){
  var b = i + 1, c = b/2, e = c.toString();
     if(e.indexOf('.') != -1 ) {
     empty[el] = arr[i+1];
  } 
}); 
  console.log(empty)

Comments

2
const text = '{"name":"John", "age":30, "city":"New York"}';
const myArr = JSON.parse(text);
document.getElementById("demo").innerHTML = myArr.name;

1 Comment

Your quoting is different that the original question.
2

As already mentioned:

  • JSON.parse does not accept unquoted keys.
  • JSON5 is very robust

My requirements:

  • JSON5's bundle size is 31.82KB -> 9.77KB (gzip). I needed something more lightweight
  • I needed to process any user-defined string. You may not need this.

Function:

export const deserialize = <R>(arg?: string | null): R => {

  // IS THE STRING NULL OR UNDEFINED?
  if (arg === null || arg === undefined) {
    return arg as R
  }

  // IS THE STRING 'undefined'?
  if (arg === 'undefined') {
    return undefined as R
  }

  // IS THE STRING EMPTY?
  if (arg === '') {
    return arg as R
  }

  // IS THE STRING A NUMBER?
  if (!isNaN(Number(arg))) {
    return parseFloat(arg) as R;
  }

  // IS THE STRING A BOOLEAN?
  if (arg === 'true') {
    return true as R
  }
  if (arg === 'false') {
    return false as R
  }

  // IS THE STRING JSON?
  try {
    const potentiallyParsableJson = arg
      // wrap all strings wrapped in single quotes with double quotes
      .replace(/'([^']+)'/g, '"$1"')
      // wrap all unquoted keys in double quotes
      .replace(/([{,]\s*)([a-zA-Z0-9_]+?)\s*:/g, '$1"$2":')
      // remove all trailing commas
      .replace(/,\s*}/g, '}').replace(/,\s*]/g, ']')

    return JSON.parse(potentiallyParsableJson)
  } catch (e) {

    // WE'VE RUN OUT OF OPTIONS, JUST RETURN THE STRING
    return arg as R
  }
}

Test cases:

describe('deserialize', () => {

  it('should handle null', () => {
    expect(deserialize(null)).toEqual(null);
  })

  it('should handle undefined', () => {
    expect(deserialize()).toEqual(undefined);
  })

  it(`should handle 'null'`, () => {
    expect(deserialize('null')).toEqual(null);
  })

  it(`should handle 'undefined'`, () => {
    expect(deserialize('undefined')).toEqual(undefined);
  })

  it('should handle empty strings', () => {
    expect(deserialize('')).toEqual('');
  })

  it('should handle normal strings', () => {
    expect(deserialize('test')).toEqual('test');
  })

  it('should handle numbers', () => {
    expect(deserialize('33')).toEqual(33);
  })

  it('should handle a value of "true"', () => {
    expect(deserialize('true')).toEqual(true);
  })

  it('should handle a value of "false"', () => {
    expect(deserialize('false')).toEqual(false);
  })

  it('should handle a shallow object', () => {
    expect(deserialize(`{ str: 'str', boo: true, num: 3 }`)).toEqual({ str: "str", boo: true, num: 3 });
  })

  it('should handle a deep object', () => {
    expect(deserialize(`{ str: 'str', boo: true, num: 3, arr: [1, 2, 3], obj: { one: { two: { three: 3 } } } }`))
      .toEqual({ str: "str", boo: true, num: 3, arr: [1, 2, 3], obj: { one: { two: { three: 3 } } } });
  })

  it('should simply return the same string if it is not valid JSON', () => {
    expect(deserialize(`{ str: str, boo: true }`)).toEqual(`{ str: str, boo: true }`);
  })

  it('arrays may have a tailing comma', () => {
    expect(deserialize(`{ arr: [1, 2, 3], }`)).toEqual({ arr: [1, 2, 3] })
  })

  it('objects may have a tailing comma', () => {
    expect(deserialize(`{ obj: {one: 'test'}, }`)).toEqual({ obj: { one: 'test' } })
  })

});

Comments

1

Here is my approach to handle some edge cases like having whitespaces and other primitive types as values

const str = " c:234 , d:sdfg ,e: true, f:null, g: undefined, h:name "; 

const strToObj = str
  .trim()
  .split(",")
  .reduce((acc, item) => {
    const [key, val = ""] = item.trim().split(":");
    let newVal = val.trim();

    if (newVal == "null") {
      newVal = null;
    } else if (newVal == "undefined") {
      newVal = void 0;
    } else if (!Number.isNaN(Number(newVal))) {
      newVal = Number(newVal);
    }else if (newVal == "true" || newVal == "false") {
      newVal = Boolean(newVal);
    }
    return { ...acc, [key.trim()]: newVal };
  }, {});

Comments

1

You don't have to always convert to JSON

So here "person begin as a string!" Finally, "person is converted to object", no necessarily to JSON.

function strToObj(e){if(typeof e=="string"){ let obj=new Function("return" +e); try{return obj()}catch{console.log("Fix, String no allowed to object")}}else{console.log("it is not a string") } };
//Example, person is a string
  let person='{firstName:"John", lastName:"Doe", id: 55, fullName:function(){return this.firstName+" "+this.lastName} }';
console.log(strToObj(person));

And it run functions internal to the object without major issues if it is called:

person=strToObj(person); console.log(person.fullName())

Simply, string = "firstName:name1, lastName:last1";

let string = "firstName:name1, lastName:last1"; 
let object= strToObj("{"+string+"}");
console.log(object)

1 Comment

This is the actual correct answer, and no eval or JSON requirement.
0

In your case

var KeyVal = string.split(", ");
var obj = {};
var i;
for (i in KeyVal) {
    KeyVal[i] = KeyVal[i].split(":");
    obj[eval(KeyVal[i][0])] = eval(KeyVal[i][1]);
}

2 Comments

eval should be avoided at all costs.
@Alex It be avoided when it can be infected by user-input.
0
var stringExample = "firstName:name1, lastName:last1 | firstName:name2, lastName:last2";    

var initial_arr_objects = stringExample.split("|");
    var objects =[];
    initial_arr_objects.map((e) => {
          var string = e;
          var fields = string.split(','),fieldObject = {};
        if( typeof fields === 'object') {
           fields.forEach(function(field) {
              var c = field.split(':');
              fieldObject[c[0]] = c[1]; //use parseInt if integer wanted
           });
        }
            console.log(fieldObject)
            objects.push(fieldObject);
        });

"objects" array will have all the objects

Comments

-1

I know this is an old post but didn't see the correct answer for the question.

var jsonStrig = '{';
      var items = string.split(',');
      for (var i = 0; i < items.length; i++) {
          var current = items[i].split(':');
          jsonStrig += '"' + current[0] + '":"' + current[1] + '",';
      }
      jsonStrig = jsonStrig.substr(0, jsonStrig.length - 1);
      jsonStrig += '}';
var obj = JSON.parse(jsonStrig);
console.log(obj.firstName, obj.lastName);

Now you can use obj.firstName and obj.lastName to get the values as you could do normally with an object.

Comments

-1
class Param {
  constructor(...args) {
    this.firstName = args[0]
    this.lastName = args[1]
  }
  static parse(_, ...values) {
    return new Param(...values)
  }
}

let [name1, last1] = ["Ankit", "Anand"]

console.log(Param.parse`firstName:${name1}, lastName:${last1}`)

Comments