Convert Object to string in javascript without the quotes in key - javascript

How can I convert an Object to a string so that output is something like this:
e.g. let a = {b: "c"}
Let's assume the above example is our sample Object. Now we can use JSON.stringify(a) to convert it to string but that outputs,
console.log(a) -> {"b": "c"} but I want something like this: {b: "c"} in the original Object format.

You can try using a Reg-ex, where you replace only the first occurrence of "" with white space character using $1 in the String.prototype.replace call:
const a = JSON.stringify({a: "a", b: "b", c: "c"}).replace(/"(\w+)"\s*:/g, '$1:');
console.log(a);

This code is taken from this answer.
There is a regex solution that is simpler but it has a shortcoming that is inherent to regex. For some edge cases in complex and nested objects it does not work.
const data = {a: "b", "b": "c",
c: {a: "b", "c": "d"}}
console.log(stringify(data))
function stringify(obj_from_json){
if(typeof obj_from_json !== "object" || Array.isArray(obj_from_json)){
// not an object, stringify using native function
return JSON.stringify(obj_from_json);
}
// Implements recursive object serialization according to JSON spec
// but without quotes around the keys.
let props = Object
.keys(obj_from_json)
.map(key => `${key}:${stringify(obj_from_json[key])}`)
.join(",");
return `{${props}}`;
}

You can try javascript-stringify npm package.

Related

Append string to Array in non-mutative way in Javascript

I have function that returns a string or an array of strings depending on some rules defined inside.
So we want to basically take that String/Array and basically make sure that we end up with an array of strings with an additional string 'X' added as last item to that array.
concat is a very useful array method for that purpose, since it both accepts an array as argument or a non-array. Either way it will concatenate those/that value(s) to the array on which it is called, and return the result.
In addition, concat accepts more than one argument, so you can at the same time add some other value, like "X":
function f(b) {
if (b) return ["a", "b"];
else return "a";
}
// call f twice, the first time it returns "a", the second time ["a", "b"]
for (let b = 0; b < 2; b++) {
let res = [].concat(f(b), "X");
console.log(res);
}
function append(vec, word){
if (typeof(vec)==="string"){
vec = [vec]
}
return vec.concat(word)
}
a = "foo"
b = ["foo","bar"]
append(a,"xyz") //-> [ 'foo', 'xyz' ]
append(b,"xyz") //-> [ 'foo', 'bar', 'xyz' ]
When you're dealing with a variable that may or may not already be an array, the .flat() method comes in handy, so you can wrap it in an array and then make sure the array is only one level deep.
This is how I would achieve this, and there are two different ways you could write up this function, so I'll include both below:
const stringExample = "test";
const arrayExample = ["1", "2", "3"];
const xStrings1 = strings => [...[strings].flat(), "X"]; // option 1
const xStrings2 = strings => [strings, "X"].flat(Infinity); // option 2
console.log(xStrings1(stringExample)); // -> ["test", "X"]
console.log(xStrings2(stringExample)); // -> ["test", "X"]
console.log(xStrings1(arrayExample)); // -> ["1", "2", "3", "X"]
console.log(xStrings2(arrayExample)); // -> ["1", "2", "3", "X"]
I've benchmark-tested both functions with both examples of sample data, and they are nearly identical in performance for both types. Neither is conclusively faster than the other, so it's up to you which you would prefer to use.

Convert all key names of an object to strings

I have an object like:
obj = {"a": 1, "b": 2, c: 3}
this object is returned by a Node.js package.
I want to pass this object to a request headers but it fails because c in that object has invalid key name I guess.
Is it possible to convert all the key names of my object to strings?
You can use JSON.stringify() to convert it to a valid JSON string:
obj = JSON.stringify({"a": 1, "b": 2, c: 3});
console.log(obj)
You can create a new object and call toString() while creating the key
const obj = {
"a": 1,
"b": 2,
c: 3
};
const newObj = {};
for (let keys in obj) {
newObj[keys.toString()]: obj[keys]
}
In JavaScript, all object keys that are not symbols are converted to strings.
obj = {1: 1}
obj['1'] = 2;
// obj is {1: 2}
If you got such an 'object' from node package it means the data has not been prepared correctly - probably somebody has created JSON by hand (writing a string instead of stringify an object).
If you want to add " to all keys and change an object to a string, so, in other words, make a JSON out of it - you can use JSON.stringify(obj) //"{"a":1,"b":2,"c":3}"
If you want to make an object out of such a broken JSON:
JSON.parse(JSON.stringify(obj)) // "{a: 1, b: 2, c: 3}"

Javascript refer to index like enum, refactor

I have a const object that I refer to.
I refer to both the key and the value.
For reasons beyond me, I don't want to directly use the key (since it is an integer).
export const listObject = {
1: "a",
2: "b",
3: "c",
4: "d",
}
so when I map this, I refer to the key index as the mappedIndex, and the value as listObject[mappedIndex]
I want to refer to the key in a different way.
Would something like this work?
export const listObject = {
1: "a",
"reason1": 1,
2: "b",
"reason2": 2,
3: "c",
"reason3": 3,
4: "d",
"reason4": 4,
}
Ignore the naming convention, this was just written as an example.
so now I can refer to the key as listObject[listObject.reason1] and the value still as listObject[mappedIndex]
i might have all this mixed up, as of doing it this way I won't be able to map properly. any recommendations?
If you absolutely wish to be able to access the object like this, I would take the following approach: First, we have our base object from you:
export const listObject = {
1: "a",
2: "b",
...
}
Then, I would create a different object that contains the keys.
export const listObjectKeys = {
reason1: 1,
reason2: 2,
...
}
Now, you could access a property in listObject object using listObject[listObjectKeys.reason1]. I find this to be the best way to structure everything in order to not accidentally taint your listObject with extra rows that are actually just keys. Let me know if you have any questions or if I'm misunderstanding anything.
You could do something like typescript's enums. They trasnpile to something like this in javascript:
const listObject = {}
listObject[listObject["reason1"] = 1] = "a";
listObject[listObject["reason2"] = 2] = "b";
listObject[listObject["reason3"] = 3] = "c";
listObject[listObject["reason4"] = 4] = "d";
(This is same as the second example in the question)
You can delcare constant listObject as array of object as mentioned below:
const listObject = [{"index":1,"value":"a"},{"index":2,"value":"b"}];
And you can access index 1 value as bleow:
listObject[0].value => "a"

Why does {. . . .0} evaluate to {}?

I just found {....0} in friend's code. Evaluating it in console returns {} (empty object).
Why is that? What is the meaning of 4 dots in JavaScript?
Four dots actually have no meaning. ... is the spread operator, and .0 is short for 0.0.
Spreading 0 (or any number) into an object yields an empty object, therefore {}.
Three dots in an object literal are a spread property, e.g.:
const a = { b: 1, c: 1 };
const d = { ...a, e: 1 }; // { b: 1, c: 1, e: 1 }
The last dot with a 0 is a number literal .0 is the same as 0.0. Therefore this:
{ ...(0.0) }
spreads all properties of the number object into the object, however as numbers don't have any (own) properties you get back an empty object.
In a simple terms {...} spread operator in javascript extends one object/array with another.
So, when babelifier tries extending one with another, it has to identify whether it is trying to extend an array or an object.
In the case of array, it iterates over elements.
In the case of object, it iterates over keys.
In this scenario, the babelyfier is trying to extract keys for number by checking the Object's own property call which is missing for number so it returns empty Object.
Spread operator {...} allows iterables to expand. It means that those data types that can be defined in form of key-value pairs can be expanded. In terms of Object we call key-value pair as Object property and it's value whereas in terms of arrays we can think index as key and element in array as it's value.
let obj = { a: 4, b: 1};
let obj2 = { ...obj, c: 2, d: 4}; // {a: 4, b: 1, c: 2, d: 4}
let arr1 = ['1', '2'];
let obj3 = { ...arr1, ...['3']}; // {0: "3", 1: "2"}
In terms of array, as it takes index as key so here it replaces element '1' of arr1 with '3' because both of them have same index in different array.
With strings too spread operator returns non-empty object. As string is an array of character so it treats string as an array.
let obj4 = {...'hi',...'hello'} // {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
let obj5 = {...'y',...'x'} // {0: "x" }
But with other primitive data types it return empty object
with Numbers
let obj6 = { ...0.0, ...55} // {}
with Boolean
let obj7 = { ...true, ...false} // {}
In conclusion those data types that can be treated in form of key-value pairs when used with spread operator {...} returns non-empty object otherwise it returns empty object {}

Adding array to numbers from array [duplicate]

According to the MDN JavaScript documentation you can define object literal property names using integers:
Additionally, you can use a numeric or string literal for the name of a property.
Like so:
me = {
name: "Robert Rocha",
123: 26,
origin: "Mexico"
}
My question is, how do you reference the property that has an integer as a name? I tried the usual me.123 but got an error. The only workaround that I can think of is using a for-in loop. Any suggestions?
You can reference the object's properties as you would an array and use either me[123] or me["123"]
Dot notation only works with property names that are valid identifiers. An identifier must start with a letter, $, _ or unicode escape sequence. For all other property names, you must use bracket notation.
In an object literal, the property name must be an identifier name, string literal or numeric literal (which will be converted to a string since property names must be strings):
var obj = {1:1, foo:'foo', '+=+':'+=+'};
alert(obj[1] + ' ' + obj.foo + ' ' + obj['+=+']); // 1 foo +=+
You can use me[123] or me["123"]. Both work.
You can use bracket notation me[123].
Just in case anyone else was confused by this: using integer (rather than string) property names may give slightly different - though functionally the same - results (depending on the browser) when you have objects within objects.
Simple objects with no nested objects have consistent behavior across browsers (though as the accepted answer says, we need to use brackets instead of dots to access integer property names):
var str_simple = {
a: "b", c: "d", e: "f", g: "h",
};
str_simple.a === "b"; // true
str_simple.e === "f"; // true
var int_simple = {
1: 2, 3: 4, 5: 6, 7: 8,
};
int_simple[1] === 2; // true - must use brackets instead of dots
int_simple[5] === 6; // true
// this works b/c int property names are coerced to strings anyway
int_simple[1] === int_simple['1']; // true
And this nested object with string keys works exactly as expected:
var str_nested = {
a: {b: "c"},
d: {e: "f", g: "h"},
};
str_nested.a; // returns object as expected, no matter the browser - {b: "c"}
str_nested.a.b === "c"; // true
str_nested.d.g === "h"; // true
But this equivalent nested object with integer keys returns slightly different results depending on the browser, though you can still access the nested objects in the same way (so functionally, it still works the same):
var int_nested = {
1: {2: 3},
4: {5: 6, 7: 8},
};
// latest Chrome (57)
// Safari 10 (latest for my Mac, 10.10 Yosemite)
int_nested[1]; // returns object as expected - {2: 3}
int_nested[1][2] === 3; // true
// latest Firefox (52)
int_nested[1]; // RETURNS ARRAY-LIKE OBJECT - Object [ <2 empty slots>, 3 ]
int_nested.length; // undefined because it's not technically an array
int_nested[1][2] === 3; // true - works b/c object was padded with empty slots
// and again, in all browsers, we can exchange the integer keys
// for equivalent strings since property names are coerced to strings anyway
int_nested[1][2] === int_nested['1'][2];
int_nested['1'][2] === int_nested[1]['2'];
int_nested[1]['2'] === int_nested['1']['2'];
This behavior will still be slightly different but functionally the same if you programmatically construct a nested object. For example, say we wanted to write a function that would take a list of pairs (e.g. [[0, 0], [0, 1], [1, 2], [2, 3]]) and convert it into a nested object so we could check if the pair is in the object with O(1) time (e.g. {0: {0: true, 1: true}, 1: {2: true}, 2: {3, true}}). Note that Sets check reference equality and not value equality, so we couldn't store the pair itself in the Set and achieve the same results:
// [[0, 0], [0, 1], [1, 2], [2, 3]] ->
// {
// 0: {0: true, 1: true},
// 1: {2: true},
// 2: {3: true},
// }
function createNestedObject(pairs) {
var obj = {};
for (var pair of pairs) {
var x = pair[0], y = pair[1];
// must create outer object for each unique x or else
// obj[x][y] would fail b/c obj[x] would be undefined
if (!obj.hasOwnProperty(x)) {
obj[x] = {};
}
obj[x][y] = true;
}
return obj;
}
function exists(nested, pair) {
var x = pair[0], y = pair[1];
// uses !! operator so if pair isn't in nested
// we return false instead of undefined
return !!(nested[x] && nested[x][y]);
}
Pairs with strings will work as expected:
var pairs = [["a", "a"], ["a", "b"], ["c", "d"], ["d", "e"]];
var nested = createNestedObject(pairs);
nested; // as expected - {a: {a: true, b: true}, c: {d: true}, d: {e: true}}
exists(nested, ["a", "a"]); // true
exists(nested, ["a", "b"]); // true
exists(nested, ["ZZZ", "ZZZ"]); // false
But in certain browsers, integer pairs will be different but functionally the same:
var pairs = [[0, 0], [0, 1], [1, 2], [2, 3]];
var nested = createNestedObject(pairs);
nested; // in Safari 10/Chrome 57 - returns nested objects as expected
nested; // in Firefox 52 - Object [ Object[2], Object[3], Object[4] ]
// BUT still gives correct results no matter the browser
exists(nested, [0, 0]); // true
exists(nested, [0, 1]); // true
exists(nested, ['0', '0']); // true
exists(nested, [999, 999]); // false
The situation with numeric property names seems more complicated than it is explained in the answers so far. It is true that you can access such properties via for-in loop. However, it might be important to know that for-in loop gives keys as strings, not as numbers as you might expect:
var obj = {1:2};
for (var key in obj) {
alert(typeof(obj[key])); // you get "number" as expected, however
alert(typeof(key)); // you get "string", not "number"
}
A similar thing happens during serialization with JSON:
JSON.stringify( {1:2} ) === '{"1":2}'
So if you code depends on this little detail you better be aware of it.

Categories