I would like to remove the matching elements {}, and {} from a JSON string.
Input : "test": [{},{},{},{},{},{},{}],
Output : "test": [],
To do so, I tried :
var jsonConfig = JSON.stringify(jsonObj);
var jsonFinal = jsonConfig.replace(/[{},]/g, ''); // Remove global
var jsonFinal = jsonConfig.replace(/[{},]/, ''); // Remove brackets
console.log(jsonFinal);
and many more.
How can I remove only those set of elements from my JSON without impacting the other brackets and comma?
Do NOT attempt to modify JSON with string manipulation functions.
ALWAYS parse the JSON, transform the data, and re-stringify to JSON.
EDIT: this answer addresses your comment that the input data object will contain other potential keys that should be present in the output.
// a couple of procedures to help us transform the data
const isEmptyObject = x => Object.keys(x).length === 0;
const not = x => ! x;
const comp = f => g => x => f (g (x));
const remove = f => xs => xs.filter (comp (not) (f));
// your input json
let json = '{"test": [{},{},{"x": 1}], "test2": [{},{}], "a": 1, "b": 2}';
// parsed json
let data = JSON.parse(json);
// transform data
let output = JSON.stringify(Object.assign({}, data, {
// remove all empty objects from `test`
test: remove (isEmptyObject) (data.test),
// remove all empty objects from `test2`
test2: remove (isEmptyObject) (data.test2),
}));
// display output
console.log(output); // '{"test":[{"x":1}],"test2":[],"a":1,"b":2}'
I like the ES2015 answer of #naomik.
This is another alternative:
/**
* Remove empty objects or arrays
* #param {Object, Array} obj: the object to which remove empty objects or arrays
* #return {Any}
*/
const removeEmptyObject = (function() {
const isNotObject = v => v === null || typeof v !== "object";
const isEmpty = o => Object.keys(o).length === 0;
return function(obj) {
if (isNotObject(obj)) return obj;
if (obj instanceof Array) {
for (let i = 0; i < obj.length; i += 1) {
if (isNotObject(obj[i])) continue;
if (isEmpty(obj[i])) obj.splice(i--, 1);
else obj[i] = removeEmptyObject(obj[i]);
}
}
else {
for (let p in obj) {
if (isNotObject(obj[p])) continue;
if (!isEmpty(obj[p])) obj[p] = removeEmptyObject(obj[p]);
if (isEmpty(obj[p])) delete obj[p];
}
}
return obj;
}
}());
Now lets test the code:
var json = '{"test": [{},{},{"x": 1}], "test2": [{},{}], "test3":[[],[1,2,3],[]], "a": 1, "b": 2}';
var data = JSON.parse(json); //Object
var output = removeEmptyObject(data);
console.log(output);
console.log(removeEmptyObject(9));
console.log(removeEmptyObject(null));
console.log(removeEmptyObject({}));
You should work on the actual object not the string.
If you do, you can loop through the object and check if it has any properties. If it doesn't have any, you can remove it.
for(var prop in obj) {
if (obj.hasOwnProperty(prop)) {
//remove here
}
}
Setting aside the question of whether string manipulation is the best way to tidy up JSON data, your earlier attempts would remove all braces and commas, because [] in a regexp indicates "match any of the characters contained inside these brackets". If you were trying to treat those as literal characters, they'd need to be escaped: \[ or \]
You want something like .replace(/{},?/g,"") (which means "match all instances of the string {} or the string {}, -- the question mark makes the preceding character an optional match).
(This would, of course, remove all empty objects from the string, and has the potential to create invalid JSON given input like "foo: {}, bar: {}" -- so only use this if you're certain that your data will never include intentionally empty objects.)
Related
I have a json from Google pagespeed api, which needs to be exported to big query. Since BQ doesnt support the keys having - symbol, I must replace all the key names those have - char to _
Please note that I cannot perform a find and replace on entire string as the values need a - char. The json structure can be complex and I understand the only ways to do is by iterating all the keys of nested objects.
I found this https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
but could not iterate the nested json, as I cannot predetermine the json structure and all that must be dynamically decided Any pointers how to replace all chars would be great
The input JSON is here: https://drive.google.com/file/d/1lFYW26xsPGQp6WU9h6f2-E1bHW-hDIic/view?usp=sharing
const replaceKeys = obj => {
return Object.fromEntries(Object.entries(obj).map( ([key,value]) => {
return [
key.replace("-","_"),
Array.isArray(value)
? value.map(replaceKeys)
:typeof value == "object"
? replaceKeys(value)
: value
];
}))
}
const result = input.map(replaceKeys);
console.log(result);
You can do this transformation both, when you JSON.parse() the data from google, or when you JSON.stringify() the object to send it to big query.
imo. that's the most economical approach, to transform while parsing or serializing, instead of first parsing then transforming or first transforming then serializing.
// sources
const obj = {
"key-with-dashes": [{
"some-value": 42
}]
};
const json = JSON.stringify(obj);
// utilities
const includesDash = v => v.includes("-");
const replaceDashInKey = kv => {
kv[0] = kv[0].replace(/-/g, "_");
return kv;
}
const cleanupDashesInKeys = (k, v) => {
if (typeof v === "object" &&
v !== null &&
!Array.isArray(v) &&
Object.keys(v).some(includesDash)
) {
return Object.fromEntries(Object.entries(v).map(replaceDashInKey))
}
// this value has no keys with dashes
return v;
};
//cleanup keys
console.log("on parse", JSON.parse(json, cleanupDashesInKeys));
console.log("on stringify", JSON.stringify(obj, cleanupDashesInKeys));
.as-console-wrapper{top:0;max-height:100%!important}
If I have the object literal:
{a: "hello"}
Is there a Javascript function to convert this object into a literal string, so that the output would be the literal syntax:
'{a: "hello"}'
With JSON.stringify the output would be
'{"a": "hello"}'
You can do it with JSON.stringify and then with String.replace like follows:
var jsObj =
{
abc: "hello",
bca: "allo",
cab: "dd:cc",
d: ["hello", "llo", "dd:cc"],
e: {abc: "hello", bca: "allo", cab: "dd:cc"}
};
function format(obj)
{
var str = JSON.stringify(obj, 0, 4),
arr = str.match(/".*?":/g);
for(var i = 0; i < arr.length; i++)
str = str.replace(arr[i], arr[i].replace(/"/g,''));
return str;
}
console.log(format(jsObj));
JavaScript has no built-in functions that will convert an object to a string representation of it which either:
Uses identifiers instead of strings for property names
Represents the original syntax used to create the object
You could write your own function for the former (at least when the property name can be represented as a literal) but the latter is impossible as JavaScript stores no information about the source code used to create the object in the first place.
Ok just for fun...roll your own?
const stringify = (obj) => {
// Iterate over keys, reducing to a string
let str = Object.keys(obj).reduce((acc, cur) => {
let next = `${cur}: "${obj[cur]}"`;
return acc
? `${acc}, ${next}`
: `{${next}`;
}, '');
// Return, appending final '}'
return `${str}}`;
}
document.write(stringify({
foo:1,
bar:'seat'
}));
That said, your exact requirements aren't clear so I'm not sure this will meet them. But it might be a starting point if there's no native solution that works.
It does convert it to the literal syntax. You are able to create objects with multiple forms of syntax. Both of the following object declarations are valid:
var a = {a: "a"}
var b = {"b": "b"}
If you want to remove the "" around the key you should be able to match them with the following regex /\"(.*?)\":/g and replace them with something like this:
function reformat(str) {
var myRegexp = /\"(.*?)\":/g;
match = myRegexp.exec(str);
while (match != null) {
str = str.replace(match[0], match[1] + ":");
match = myRegexp.exec(str);
}
return str;
}
Hope that helps :)
This question already has answers here:
Convert a JavaScript string in dot notation into an object reference
(34 answers)
Closed 5 years ago.
I have csv files that I am reading using nodeJS. I convert each file to text before reading.
Each line in the file have data delimited with =.
Each line looks something like
data.location.degree.text=sometexthere
The first portion before the "=" represents an index to a JSON object in my app. My aim is to parse this data and build a JSON representation of it so that the line above becomes
data:{
location:{
degree:{
text: 'sometexthere'
}
}
}
Using javascript/nodejs; How can I convert a string which is supposed to represent a sequence of nested JSON keys, into a JSON object like above?
You could split the path and make a check if the following element exist. If not assign an object to the new property.
Return then the value of the property.
At the end assign the value.
function setValue(object, path, value) {
path = path.replace(/[\[]/gm, '.').replace(/[\]]/gm, ''); //to accept [index]
var keys = path.split('.'),
last = keys.pop();
keys.reduce(function (o, k) { return o[k] = o[k] || {}; }, object)[last] = value;
}
var data = {};
setValue(data, 'location.degree.text', 'sometexthere');
console.log(data);
// result container
var res = {};
// input data
var inp = [
'data.location.degree.text=sometexthere',
'data.otherLocation.degree.otherText=foo',
'data.location.degree.otherText=bar',
'we.can.handle.values.that.are_undefined=',
'we.can.handle.values.that.contain_equals_signs=yes=we=can'
];
// recursive function
var pathToObject = function(resultReference, path)
{
// split path on dots
// e.g. data.location.degree.text=sometexthere
// -> ["data", "location", "degree", "text=sometexthere"]
var splitPathParts = path.split('.');
// if there is only one part, we're at the end of our path expression
// e.g. ["text=sometexthere"]
if (splitPathParts.length === 1){
// split "text=sometexthere" into ["text", "sometexthere"]
var keyAndValue = splitPathParts[0].split('=');
// set foo = bar on our result object reference
resultReference[keyAndValue.shift()] = keyAndValue.join('=');
return;
}
// the first element of the split array is our current key
// e.g. for ["data", "location", "degree", "text=sometexthere"],
// the currentKey would be "data";
var currentKey = splitPathParts.shift();
// if our object does not yet contain the current key, set it to an empty object
resultReference[currentKey] || (resultReference[currentKey] = {});
// recursively call ourselves, passing in
// the nested scope and the rest of the path.
// e.g. { data : {} } and 'location.degree.text=sometexthere'
pathToObject(resultReference[currentKey], splitPathParts.join('.'));
}
for (var i = 0; i < inp.length; i++)
{
pathToObject(res, inp[i]);
}
console.log(res);
ES6 syntax makes things slightly more terse:
'use strict';
const pathToObject = (resultReference, path) => {
let [currentKey, ...restOfPath] = path.split('.');
if (restOfPath.length === 0) {
let [k, ...v] = currentKey.split('=');
resultReference[k] = v.join('=');
return;
}
resultReference[currentKey] || (resultReference[currentKey] = {});
pathToObject(resultReference[currentKey], restOfPath.join('.'));
}
let res = {};
[
'data.location.degree.text=sometexthere',
'data.otherLocation.degree.otherText=foo',
'data.location.degree.otherText=bar',
'we.can.handle.values.that.are_undefined=',
'we.can.handle.values.that.contain_equals_signs=yes=we=can'
].forEach(x => pathToObject(res, x));
console.log(res);
I have a string example
"abc|pqr[abc,xyz[abc,def]]"
Now i want to output into array
{
abc : true,
pqr : ['abc', xyz : [abc, def]]
}
the code i wrote is this but it give me
"message": "Maximum call stack size exceeded"
var x = 'p[a,b,c,d]|q[small,large]|r[small,large]|s|t[w[x,y],z[a,b,c]]';
y = x.split("|");
function foo(query) {
if (typeof query == "string") query = [query]
var i = {}
_(query).forEach(function(v) {
regexQuery = v.match(/\[(.*)\]/);
if (regexQuery != null) {
index = regexQuery['index']
if (regexQuery[1].match(/\[(.*)\]/) != null) {
i[regexQuery['input'].substr(0, index)] = foo(regexQuery[0])
} else {
i[regexQuery['input'].substr(0, index)] = regexQuery[1].split(",");
}
} else {
i[v] = true;
}
})
return i;
}
console.log(foo(y));
i know regex is not got for this but is there any other solution?
You could use the function below. For the input given in the question:
p[a,b,c,d]|q[small,large]|r[small,large]|s|t[w[x,y],z[a,b,c]]
...it produces this object:
{
"p": [
"a",
"b",
"c",
"d"
],
"q": [
"small",
"large"
],
"r": [
"small",
"large"
],
"s": true,
"t": {
"w": [
"x",
"y"
],
"z": [
"a",
"b",
"c"
]
}
}
function toObject(x) {
// Turn custom format into JSON text format, and then parse it.
// In that object, find nested objects that could be turned into array.
return (function flagsToArray(obj) {
// Collect keys with nested objects.
var nested = Object.keys(obj).filter(key => obj[key] !== true);
// For those, call this function recursively
nested.forEach(key => obj[key] = flagsToArray(obj[key]));
// If no nesting, then turn this into an array
return nested.length ? obj : Object.keys(obj);
})(JSON.parse('{' +
x.replace(/\|/g, ',') // treat '|' as ','
.replace(/"/g, '\"') // escape any double quotes
.replace(/([^,|\[\]]+)/g, '"$1"') // wrap terms in double quotes
.replace(/"\[/g, '":[') // insert colon for assignment of arrays
.replace(/"([,\]])/g, '":true$1') // insert `true` assignment for atomic term
.replace(/\[/g, "{").replace(/\]/g, "}") // replace array notation with object notation
+ '}'));
}
// Sample input
var x = 'p[a,b,c,d]|q[small,large]|r[small,large]|s|t[w[x,y],z[a,b,c]]';
// Convert
var obj = toObject(x);
// Output
console.log(obj);
The function makes several replacements to convert the custom format into a JSON text format, turning everything into nested objects (no arrays). Then in a second process, a recursive one, objects are identified that have no nested objects, i.e. they only consist of members with true as value. Those objects are then replaced by their array "equivalent", i.e. the array with the object's keys.
Your query string is essentially a flat representation of a tree whose nodes are defined by:
either a name alone
or a name and a list of child nodes
Note that I don't see any obvious difference between , and |, so I'm going to assume that they actually have the same meaning.
You can't easily store this structure by using only arrays, and it would also be unnecessarily complicated to use a mix of arrays and objects.
Therefore, I'd suggest to use only objects with the following conventions:
key = name of node
value = either true1 or a child object
1 This is a placeholder. You may also consider using an empty object.
With these assumptions, your example string "abc|pqr[abc,xyz[abc,def]]" would be decoded as:
tree = {
"abc": true,
"pqr": {
"abc": true,
"xyz": {
"abc": true,
"def": true
}
}
}
Such a structure is quite easy to manipulate.
For instance, if you'd like to get the child nodes of root > pqr > xyz, you could do:
Object.keys(tree.pqr.xyz)
which will return:
["abc", "def"]
Implementation
Below is a possible implementation:
function parse(query) {
var n, tree = {}, node = tree, stk = [],
sym = '', sz = (query += ',').length;
for(n = 0; n < sz; n++) {
switch(query[n]) {
case '|':
case ',':
sym && (node[sym] = true);
break;
case '[':
stk.push(node);
node = node[sym] = {};
break;
case ']':
sym && (node[sym] = true);
node = stk.pop();
break;
default:
sym += query[n];
continue;
}
sym = '';
}
return tree;
}
console.log(parse("abc|pqr[abc,xyz[abc,def]]"));
I have a JSON file format as follows:
[{"key/1":"Value1", "key/2":"Value2" },
{"key/1.1":"Value1.1", "key/2.1":"Value2.1" },
{"key/1.2":"Value1.2", "key/2.2":"Value2.2" },
{"key/1.3":"Value1.3", "key/2.3":"Value2.3" }]
My requirement is to search all exixting key names in above JSON format and repalce the slash("/") character to some othere character to have the new JSON file with changed Key names with new replaced character.
Please help
Thanks
Using this function, you can clone the object. Just modify it a little to replace key slashes on the fly.
Open your console and run it
function replaceSlashInKeys(obj) {
if(obj == null || typeof(obj) != 'object') return obj;
var temp = obj.constructor();
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
// Replace any slash with an underscore (in the key)
temp[key.replace(/\//g, '_')] = replaceSlashInKeys(obj[key]);
}
}
return temp;
}
// Example usage
var test = [{"key/1":"Value1", "key/2":"Value2" },{"key/1.1":"Value1.1", "key/2.1":"Value2.1" },{"key/1.2":"Value1.2", "key/2.2":"Value2.2" },{"key/1.3":"Value1.3", "key/2.3":"Value2.3" }];
// Got slashes
console.log(test);
// Replace them
test = replaceSlashInKeys(test);
// Got underscores
console.log(test);
Another approach using the reduce function could be:
for (var i = 0; i < test.length; i++){
Object.keys(test[i]).reduce(function (a, b) {
test[i][b.toString().replace('/', '-')] = test[i][b];
delete test[i][b];
}, 1);
}
jsfiddle:
https://jsfiddle.net/fwp7zj3k/