Cadena para objetar en JS

190

Tengo una cuerda como

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

ahora necesito un objeto obj tal que

obj = {firstName:name1, lastName:last1}

¿Cómo puedo hacer esto en JS?

rahul
fuente
1
¿Son los valores o identificadores de cadena name1 y last1 que se han definido en otra parte?
cdleary
1
Se necesitan más datos ... ¿qué está haciendo si la clave o el valor contienen una coma o dos puntos?
Brad
Si su cadena no está formateada como JSON, es posible que deba usar RegEp para el controlador.
bitfishxyz

Respuestas:

165

En realidad, la mejor solución es usar JSON:

Documentación

JSON.parse(text[, reviver]);

Ejemplos:

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) Pasar una función a 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();
Matej
fuente
sin embargo, no permitirá que se pasen funciones
K2xL
2
Eso es cierto, sin embargo, estrictamente hablando, las funciones no deberían estar en ningún objeto JSON. Para eso tiene RPC, etc., o si lo desea, puede pasar el prototipo de una función al json, y hacerlo evalmás tarde.
Matej
36
No responde a la pregunta en absoluto, me temo. Sería maravilloso si todos cambiaran la pregunta para adaptarla a la respuesta. ¿Cómo se analiza "firstName: name1, lastName: last1"? no '{"hola": "mundo"}'.
Noel Abrahams
33
Esto no responde a la pregunta, no sé por qué tiene tantos votos a favor. El interrogador (y yo mismo, busqué en Google y terminé aquí) tiene datos que no se ajustan al estándar JSON, por lo que no se pueden analizar.
Aaron Greenwald
1
var a = "nombre: nombre1, apellido: apellido1"; JSON.parse ('{' + a + '}') arroja un error.
Aaron Greenwald
73

Su cadena se ve como una cadena JSON sin las llaves.

Esto debería funcionar entonces:

obj = eval('({' + str + '})');
Philippe Leybaert
fuente
22
Este es un potencial agujero de seguridad. No recomendaría este método en absoluto.
Breton
65
Depende de dónde provienen los datos. Algunas personas están obsesionadas con la seguridad. Si controla sus datos, puede ser más pragmático en la búsqueda de soluciones. ¿O tiene una puerta antirrobo entre su cocina y la sala de estar?
Philippe Leybaert
13
Eso no funciona Le da el error 'SyntaxError: token inesperado:' . Lo he comprobado en el Chrome.
Nyambaa
12
La solución necesita paréntesis:obj = eval('({' + str + '})');
Christian
12
prc322: Todo el mundo sabe que eval () no es muy seguro (y eso es un eufemismo), pero usar eval () es la única respuesta correcta a la pregunta anterior porque la cadena de entrada no es una cadena JSON válida Y la cadena de entrada hace referencia a otras variables por nombre.
Philippe Leybaert
50

Si estoy entendiendo correctamente:

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

Supongo que el nombre de la propiedad está a la izquierda de los dos puntos y el valor de cadena que toma está a la derecha.

Tenga en cuenta que Array.forEaches JavaScript 1.6: puede utilizar un kit de herramientas para obtener la máxima compatibilidad.

cdleary
fuente
Hola Cdleary ... Me pregunto si puedes ayudarme: stackoverflow.com/questions/9357320/… Lo siento, no pude encontrar otra manera de contactarlo, excepto a través de comentarios sobre sus respuestas
Jason
1
Puesto de prefecto. Esta respuesta utiliza los conceptos básicos de JavaScript muy claros y, por lo tanto, debería funcionar en todos los navegadores
Michel
Encontré que este enfoque fue un buen comienzo, por lo que la respuesta fue útil. Sin embargo, xecute tiene un punto válido de que no admite cadenas que pueden tener comas. Podría seguir el camino de las comillas coincidentes si su código necesita manejar cadenas con comas, pero aún así no detectará casos en los que se escapen las comillas. Simplemente implemente lo que necesita, luego deténgase.
Patrick Graham
1
En una cuestión de análisis del texto presentado por el autor de la pregunta, las preguntas de "qué pasaría si" son inútiles. Podemos aceptar el ejemplo dado como representante de los datos o simplemente no podemos responder ya que siempre podría haber un "qué pasaría si" que interrumpiría el análisis.
1
El póster preguntaba cómo analizar una estructura inusual que no era JSON válida. @cdleary respondió muy bien la pregunta dadas las circunstancias. xecute: manejar una coma en una cadena requeriría un mecanismo de comillas o escape, más allá del alcance de esta discusión.
Suncat2000
37

De esta manera simple ...

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

salida

name1
Wianto Wie
fuente
Vota a favor de esto, SIEMPRE: var string = "{firstName: 'name1', lastName: 'last1', f: function () {alert ('fuiste pirateado ...')} ()}"; eval ('var obj' + string) Esto podría hacerte hackear ... pero creo que vale la pena porque es lo único que funciona en algunos casos.
Cody
12

si estás usando JQuery:

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

RECUERDE: eval es malvado! :RE

mzalazar
fuente
77
jQuery utiliza eval. globalEval: /** code **/ window[ "eval" ].call( window, data ); /** more code **/
SomeShinyObject
12
La cadena, proporcionada por el propietario de la pregunta, no es una cadena JSON válida. Entonces, este código es inútil ...
xecute
sí, eval no es malo si lo usa en un entorno "controlado" (cualquier cosa que no sean datos externos como archivos, red o entrada del usuario, en esos casos podría ser peligroso si no se "filtra / valida").
mzalazar
11

Dado que el método JSON.parse () requiere que las claves Object estén encerradas entre comillas para que funcione correctamente, primero tendríamos que convertir la cadena en una cadena con formato JSON antes de llamar al método JSON.parse ().

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

Esto funcionaría incluso si la cadena tiene un objeto complejo (como el siguiente) y todavía se convertiría correctamente. Solo asegúrate de que la cadena esté encerrada entre comillas simples.

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

Faisal Chishti
fuente
10

Necesita usar JSON.parse () para convertir String en un objeto:

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

Si tiene una cadena como foo: 1, bar: 2puede convertirla en un obj válido con:

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

Gracias a niggler en #javascript por eso.

Actualización con explicaciones:

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)

Documentos MDN confusos:

Demostración: http://jsbin.com/hiduhijevu/edit?js,console

Función:

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!
corysimmons
fuente
Esta es una respuesta totalmente incomprensible para el lector promedio. ¿Puedes explicar qué hace cada línea? ¿Y cuál es la salida resultante, dada la entrada de OP?
not2qubit
1
Lo suficientemente justo. Por lo general, no tengo tiempo para detallar cosas y solo estoy soltando partes útiles (específicamente en caso de que me encuentre con la misma pregunta SO en el futuro), pero me equivoqué.
corysimmons
2
Esta es definitivamente la solución más elegante y en realidad responde la pregunta a diferencia de muchas de las otras respuestas. Y gracias por la explicación!
Cathy Ha
4

Implementé una solución en unas pocas líneas de código que funciona de manera bastante confiable.

Tener un elemento HTML como este donde quiero pasar opciones personalizadas:

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

una función analiza las opciones personalizadas y devuelve un objeto para usarlo en alguna parte:

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')));
rodu
fuente
+1 por usar el data-atributo en lugar de crear atributos pseudo-personalizados como algunos frameworks / bibliotecas.
John
3
string = "firstName:name1, lastName:last1";

Esto funcionará:

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

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

Sin embargo, no es eficiente. Qué sucede cuando tienes algo como esto:

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

split()dividirá 'http'. Así que te sugiero que uses un delimitador especial como tubería

 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];
       });
    }
Emeka Mbah
fuente
2
Solo una idea: en lugar de la segunda división (':'), podría dividir la cadena "manualmente" por PRIMER colon utilizando indexOf (":") y considerar la parte de la cadena hasta este colon como clave y el resto después del colon como el valor.
Ondrej Vencovsky
2

Este es un código universal, no importa qué tan larga sea su entrada, sino en el mismo esquema si existe: separador :)

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)
panatoni
fuente
2

Estoy usando JSON5 , y funciona bastante bien.

Lo bueno es que contiene no eval y no new Function , muy seguro de usar.

James Yang
fuente
0

En tu caso

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]);
}
Vahag Chakhoyan
fuente
3
eval debe evitarse a toda costa.
Alex
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);
        });

El conjunto de "objetos" tendrá todos los objetos

comeOnGetIt
fuente
0

Sé que esta es una publicación antigua, pero no vi la respuesta correcta para la pregunta.

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);

Ahora puede usar obj.firstNamey obj.lastNamepara obtener los valores como lo haría normalmente con un objeto.

Raoul
fuente