Converting array to valid JSON string without using JSON.stringify? - javascript

I'm trying to write a function that takes some object, for example a number, a string, a list, or a map (of key-value pairs); and returns a valid JSON representation of that input as a string.
I have already set up other json encoders for simple numbers and string inputs:
Input => Output
a number with value 123 => 123 (string)
a string with value abc => "abc" (string)
But I'm having issues converting an array such as [1,"2",3]
Input => Output
1,2,three array => [1,2,"three"] (string)
Here is my current code:
var my_json_encode = function(input) {
if(typeof(input) === "string"){
return '"'+input+'"'
}
if(typeof(input) === "number"){
return `${input}`
}
//This is causing my issue
if(Array.isArray(input)) {
console.log(input)
}
I could simply add and return JSON.stringify(input) to change it but I don't want to use that.
I know I can create some sort recursive solution as I have base cases set up for numbers and strings. I'm blocked on this and any help will be appreciated
Edit: So the solution as been provided below in the answers section! Thanks :)

For arrays take a recursive approach with the items.
const
json_encode = (input) => {
if (typeof input === "string") return `"${input}"`;
if (typeof input === "number") return `${input}`;
if (Array.isArray(input)) return `[${input.map(json_encode)}]`;
};
console.log(json_encode([1, 'foo', [2, 3]]));
console.log(JSON.parse(json_encode([1, 'foo', [2, 3]])));

You already have the function that converts scalar values to json values.
So, you can call this function for all array's member (e.g. using https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and then join it (https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/join) and add '[' and ']' to the resulting string
PS: this approach will work also in situations when you have an array of arrays
Example of the implementation:
var my_json_encode = function(input) {
if(typeof(input) === "string"){
return '"'+input+'"'
}
if(typeof(input) === "number"){
return `${input}`
}
if(Array.isArray(input)) {
const formatedArrayMembers = input.map(value => my_json_encode(value)).join(',');
return `[${formatedArrayMembers}]`;
}
}

Related

Getting issue while checking the content is json type using Javascript [duplicate]

This question already has answers here:
How to check if a string is a valid JSON string?
(27 answers)
Closed 1 year ago.
I am checking if the input value is json type or not using JavaScript but for some particular value its failing. I am explaining my code below.
isJSON = async(str) => {
try {
return (JSON.parse(str) && !!str);
} catch (e) {
return false;
}
}
var testJSON = '{"name": "foo"}'
var number = 22
console.log("result is : ", isJSON(testJSON))
console.log("result is : ", isJSON(number))
Here str contains the input value and this method is checking that value is json type or not. But if the input value is any number i.e-str=22 then also its returning true. Here I need to check only the input value is json type value or not. Please help me to resolve the issue.
JSON.parse will coerce values into strings.
So the only other check you need to do is to test if the value is a string or not.
if (typeof str !== "string") return false;
The string "22" is a valid JSON text.
If you want to check that the value is not only JSON but a JSON representation of a non-array object. Then you would need to test for that explicitly.
isObjectAsJSON = (str) => {
if (typeof str !== "string") return false;
try {
const parsed = JSON.parse(str);
if (typeof parsed !== "object") return false;
if (Array.isArray(parsed)) return false;
return true;
} catch (e) {
return false;
}
};
const values = {
number: 22,
json_number: JSON.stringify(22),
json_array: JSON.stringify(["foo", "bar"]),
json_object: JSON.stringify({
foo: "bar"
}),
};
Object.entries(values).forEach(([key, value]) => {
console.log(key, isObjectAsJSON(value))
});
The string 22 is valid json (json is an element, element is a ws value ws, and value can be a number) per https://www.json.org/json-en.html.

How to determine type of an expression inside string in Javascript?

I have one rest call that gives me information from query parameters and I need to determine whether they are a string or an int, array, boolean.
For example, if I have:
/.../something?id=1
I'll receive '1', but I know that's an integer.
Furthermore, I have:
/.../something?id=[1,2,3]
I'll receive '[1,2,3]' but I know it's an array. Finally, if I have:
/.../something?id=string
I'll receive 'string' and I should use it as a regular string.
Is regex the only way of doing that check for each type?
You can use JSON.parse with a catch block.
var options = ['12', '[1,2,3]', 'string', 'false', '{"x" : 2}', '/hey/', /hey/];
var parsed = options.map(x => {
try {
return JSON.parse(x)
} catch(e) {
return x;
}
});
var types = parsed.map(x => /\[object (.*)\]$/.exec(Object.prototype.toString.call(x))[1])
console.log(types);
Once you have a value
var value = "1"; //or whatever value after `id=`
you can apply this logic
var type = "";
var isNum = ( s ) => !isNaN(s);
var isObject = ( s ) => {
try { s = JSON.parse(s); return Array.isArray(s) ? "array" : "object" } catch( e ){ return false }
};
Now use them as
type = isNum( value ) ? "number" : ( isObject( s ) || "string" );
Note
If value is a function definition, it will still return a string
One way to solve this is to use trial-and-error methodologies:
If you have the value starting from [ and ending with ], you probably can assume that its an array. Use JSON.parse to be sure.
If first case doesn't match, use /^\d+$/ to test the string against integer values. Use parseInt() to be sure.
Lastly, if above both test cases fail, you can be sure that its a string, because strings can hold almost all type of values.
Savio,
One way you can handle it is to fetch the value and use switch/case statement with 'typeof' to see what they are.
Typeof explanation on MDN
// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
// Strings
typeof 'bla' === 'string';
// Booleans
typeof true === 'boolean';
// use Array.isArray or Object.prototype.toString.call
// to differentiate regular objects from arrays
typeof [1, 2, 4] === 'object';

How do I use JSON Stringify to replace all keys and values?

I'm working on a way that takes advantage of the replacer function argument in JSON.Stringify in JavaScript in order to change the word-case (toUpper /toLower case), the problem is my JSON is not straight key:value, some values are keys also and they have values themselves, so I need to go through all keys and values, check if the value is also a key and make sure I change the case (toUpper or toLower) for all keys and values.
I know that the replacer function in JSON.Stringify(object,ReplacerFunction) iterates through all keys and values and makes the modifications inside then return keys and values, but although I've been reading about this for a wWhile I can't apply it, and I am not sure if I should apply recursion inside the replacer function or how, any help is appreciated.
Code I had:
function _replacer(name,val){
if(typeof val != "object"){
return val.toString().toUpperCase()
}
if(typeof name != "object"){
return name.toString().toUpperCase()
}
console.log("key = "+name+" type: "+typeof name);
console.log("value ="+val+" type: "+typeof val);
}
Also:
function _replacer(name,val){
if(typeof val != "object" &&typeof val ==="string"){
return val=val.toUpperCase()
}
if(typeof name != "object" &&typeof name ==="string"){
name=name.toUpperCase()
}
return val;
}
Also , i eventually got to this stage :
var res = JSON.parse(JSON.stringify(j, function(key, value) {
return typeof value === "string" ? value.toUpperCase() : value
}));
but this code only capitalizes the very lower level values, not all the keys/values, the reason is because i can only return one value from the replacer function, which is in this case the value.
The replacer function in JSON.stringify, does not allow you to replace keys, as documented in MDN. It allows you to transform values, or to omit key/value pairs altogether (by returning undefined).
Your best bet is probably to transform the object before stringifying it. Underscore or Lodash would make this pretty easy, but you can do it natively without too much trouble like this:
const xform = obj => {
return Object.keys(obj).reduce((xformed, key) => {
let value = obj[key]
if (typeof value ==='string') value = value.toUpperCase()
else if (typeof value === 'object') value = xform(value)
xformed[key.toUpperCase()] = value
return xformed
}, {})
}
console.log(JSON.stringify(xform({a: 'b', c: 1, d: {e: 'f'}})))
// {"A":"B","C":1,"D":{"E":"F"}}
You could also use RegEx and replace after it is stringified if you are so inclined. The code is certainly shorter, but perhaps less readable:
const stringified = JSON.stringify({a: 'b', c: 1, d: {e: 'f'}})
console.log(stringified.replace(/".+?"/g, s => s.toUpperCase()))
// {"A":"B","C":1,"D":{"E":"F"}}

How to check if values in one JavaScript object are present in another one?

I am trying to compare json_str1 and json_str2, here it should return true as all elements in json_str1 are present in json_str2.
For now I am doing this the long way like this
json_str1 = '{"0":"a","1":"b","2":"c"}';
json_str2 = '{"0":"c","1":"b","2":"a"}';
json_obj1 = $.parseJSON(json_str1);
json_obj2 = $.parseJSON(json_str2);
arr1 = $.map(json_obj1, function(el) { return el });
arr2 = $.map(json_obj2, function(el) { return el });
if($(arr1).not(arr2).length === 0 && $(arr2).not(arr1).length === 0)
alert("equal");
else
alert("not equal");
How could I make it short and simple, without converting the objects into an array ?
https://jsfiddle.net/kq9gtdr0/
Use the following code:
Object.keys(json_obj1) . every(k1 =>
Object.keys(json_obj2) . some(k2 =>
json_obj1[k1] === json_obj2[k2]
)
);
In English:
Every key k1 in json_obj1 satisfies the condition that some key k2 in json_obj2 satisifies the condition that the value of json_obj1 with key k1 is equal to the value of json_obj2 with key k2.
Or in more conversational English:
Every value in the first object matches some value in the second.
Using lodash
var _ = require('lodash');
function compareValues(jstr1, jstr2) {
return _.isEqual(_.valuesIn(JSON.parse(jstr1)).sort(), _.valuesIn(JSON.parse(jstr2)).sort());
}
json_str1 = '{"0":"a","1":"b","2":"c"}';
json_str2 = '{"0":"c","1":"b","2":"a"}';
console.log(compareValues(json_str1, json_str2));
There is short and easy accurate way to this.
You can use a third party but extremely popular utility library called Lodash. Chaining functions you can check for equality.
First parse both JSON into objects
Then use _.values() to extract the values of all keys of each into separate arrays
Find difference of two arrays. If its an empty array then both of them are equal.
You can chain all the steps into one statement like:
_.isEmpty(_.difference(_.values(json_obj1), _.values(json_obj2)))
Example: https://jsfiddle.net/kq9gtdr0/4/
For more information:
https://lodash.com/docs#values
https://lodash.com/docs#difference
https://lodash.com/docs#isEmpty
You can include the library from CDN(https://cdn.jsdelivr.net/lodash/4.5.1/lodash.min.js) or download and use it as normal script. Lodash offers plenty of useful utility functions that makes JS programming a lot easier. You better try it out.
If you prefer using libraries, then you could use underscore isMatch
_.isMatch(object, properties)
Tells you if the keys and values in properties are contained in
object.
Extending the awesome answer by #user663031, in case you need to do deep comparison, here's code that works:
export function objectOneInsideObjectTwo(jsonObj1: any, jsonObj2: any): boolean {
return Object.keys(jsonObj1).every((k1) => {
if (parseType(jsonObj1[k1]) === 'dict') {
return objectOneInsideObjectTwo(jsonObj1[k1], jsonObj2[k1]);
}
if (parseType(jsonObj1[k1]) === 'array') {
const results: boolean[] = [];
jsonObj1[k1].forEach((o: any, i: number) => {
if (parseType(o) === 'dict') {
results.push(objectOneInsideObjectTwo(o, jsonObj2[k1][i]));
} else {
results.push(o === jsonObj2[k1][i]);
}
});
return results.every((r) => r);
}
return Object.keys(jsonObj2).some((k2) => jsonObj1[k1] === jsonObj2[k2]);
});
}
export function parseType<T>(v: T): string {
if (v === null || v === undefined) {
return 'null';
}
if (typeof v === 'object') {
if (v instanceof Array) {
return 'array';
}
if (v instanceof Date) {
return 'date';
}
return 'dict';
}
return typeof v;
}
You can try this
var json_str1 = {"0":"a","1":"b","2":"c"};
var json_str2 = {"0":"c","1":"b","2":"a"};
var flag = 1;
if(Object.keys(json_str1).length == Object.keys(json_str2).length){
Object.keys(json_str1).forEach(function(x){
if(!json_str2.hasOwnProperty(x) || json_str2[x] != json_str1[x]){
flag = 0;
return;
}
});
}
if(flag)
alert('equal');
else
alert('Not Equal');
If you want to find out if both Objects have the same keys there is no way to do this without at least converting the keys of both Objects to an array with Object.keys or looping through both Objects!
The reason is simple: It's clear that you have to compare the number of keys of both Objects and the only way to do this is by looping through all properties or Object.keys.
So I think the shortest way to do this is:
json_obj1 = JSON.parse('{"0":"a","1":"b","2":"c"}');
json_obj2 = JSON.parse('{"0":"c","1":"b","2":"a"}');
keys_1 = Object.keys(json_obj1);
keys_2 = Object.keys(json_obj2);
if(keys_1.length === keys_2.length && keys_1.every(key => keys_2.indexOf(key) >= 0)) {
alert('equal')
} else {
alert('not equal')
}
If you only want to check if all keys from json1 are present in json2 you can do:
json_obj1 = JSON.parse('{"0":"a","1":"b","2":"c"}');
json_obj2 = JSON.parse('{"0":"c","1":"b","2":"a"}');
if(Object.keys(json_obj1).every(key => key in json_obj2)) {
alert('equal');
} else {
alert('not equal');
}
In your question and comments you indicate you are only looking to verify that "all elements in json_str1 are present in json_str2". Your example code doesn't just do that, it checks for the complete equality of keys by testing if all the keys (not values) in the first object are in the second object AND all the keys in the second object are in the first object. By looking at your code, i assume that when you say "elements" you mean keys.
All that aside, this might help:
// Your first few lines of code
json_str1 = '{"0":"a","1":"b","2":"c"}';
json_str2 = '{"0":"c","1":"b","2":"a"}';
json_obj1 = $.parseJSON(json_str1);
json_obj2 = $.parseJSON(json_str2);
// My new code
var values1 = Object.keys(json_obj1).map(key => json_obj1[key]);
var values2 = Object.keys(json_obj2).map(key => json_obj2[key]);
// Check if every key in the first object is in the second object.
values1.every(k1 => values2.indexOf(k1) >= 0);
// OR
// Check for equality of keys by checking both directions.
values1.every(k1 => values2.indexOf(k1) >= 0) && values2.every(k2 => values1.indexOf(k2) >= 0);
That's 2 lines to get the keys, and one line to check. You only need one of those two checks.

Easiest Way To Return A Boolean Value For A Searched Key/Value Pair (Javascript)

I have the following JSON string...
JSON:
[{"my_id":"100002","my_confirmation":"682354682"},{"my_id":"100005","my_confirmation":"281735345"},{"my_id":"100009","my_confirmation":"361567356"}]
... which I have then parsed into a key/value pair array.
What is the easiest way to return, via Javascript, a boolean value if attempting to match "my_id" for "100005"? And also, is the parsing into an array necessary?
If you are already parsing the JSON string then in ECMA5 you could use Array.prototype.some
Javascript
var jsonString = '[{"my_id":"100002","my_confirmation":"682354682"},{"my_id":"100005","my_confirmation":"281735345"},{"my_id":"100009","my_confirmation":"361567356"}]';
var matches = JSON.parse(jsonString).some(function (obj) {
return obj.my_id === '100005';
});
console.log(matches);
Output
true
On jsFiddle
or you could go even more funky like this
function myContains(jsonStr, key, value) {
return JSON.parse(jsonStr).some(function (obj) {
return obj[key] === value;
});
}
var jsonString = '[{"my_id":"100002","my_confirmation":"682354682"},{"my_id":"100005","my_confirmation":"281735345"},{"my_id":"100009","my_confirmation":"361567356"}]';
console.log(myContains(jsonString, 'my_id', '100005'));

Categories

Resources