how to build a recursively JSON from from string in Javascript? - javascript

I have a string that could be in this form : 'school,room,teacher,pencil' or it could be 'school,room,teacher' .
What I want to do is after splitting the string , every word should have this structure :
{node:word,group:word-1,groupN:word-2 ...}
For example for the word room the structure would be {node:"room",group:"school"}
for the word teacher it would be {node:"teacher",group:"room",groupN:"school"}
for the word pencil it would be {word:"pencil",group:"teacher",groupN:"room",groupNN:"school"}
I did like that but it's not standard way :
var str="xxx,xxx,xxx,xxx"
var splitedStr = str.split(",")
for(var i=0;i<splitedStr.length;i++) {
var data = {node :splitedStr[i],group:splitedStr[i-1],groupN:splitedStr[i-2]}
}
Please do you have any idea how can i do that ?

Try this:
const getJson= (elName, textElements) => {
const elements = textElements.split(',');
const elementIdx = elements.findIndex(el => el===elName)
let result = {room:elName}
if (elementIdx>=0)
elements.slice(0,elementIdx).map((el, idx)=>{
result['group'+Array(elementIdx - idx).join("N") ] = el
})
return result
}
Ad use like this:
const arr = 'school,room,teacher,pencil'
console.log(getJson('room', arr))
console.log(getJson('teacher', arr))
console.log(getJson('pencil', arr))

Related

Apply template string dynamically in JavaScript [duplicate]

I have a string with say: My Name is %NAME% and my age is %AGE%.
%XXX% are placeholders. We need to substitute values there from an object.
Object looks like: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}
I need to parse the object and replace the string with corresponding values. So that final output will be:
My Name is Mike and my age is 26.
The whole thing has to be done either using pure javascript or jquery.
The requirements of the original question clearly couldn't benefit from string interpolation, as it seems like it's a runtime processing of arbitrary replacement keys.
However, if you just had to do string interpolation, you can use:
const str = `My name is ${replacements.name} and my age is ${replacements.age}.`
Note the backticks delimiting the string, they are required.
For an answer suiting the particular OP's requirement, you could use String.prototype.replace() for the replacements.
The following code will handle all matches and not touch ones without a replacement (so long as your replacement values are all strings, if not, see below).
var replacements = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"},
str = 'My Name is %NAME% and my age is %AGE%.';
str = str.replace(/%\w+%/g, function(all) {
return replacements[all] || all;
});
jsFiddle.
If some of your replacements are not strings, be sure they exists in the object first. If you have a format like the example, i.e. wrapped in percentage signs, you can use the in operator to achieve this.
jsFiddle.
However, if your format doesn't have a special format, i.e. any string, and your replacements object doesn't have a null prototype, use Object.prototype.hasOwnProperty(), unless you can guarantee that none of your potential replaced substrings will clash with property names on the prototype.
jsFiddle.
Otherwise, if your replacement string was 'hasOwnProperty', you would get a resultant messed up string.
jsFiddle.
As a side note, you should be called replacements an Object, not an Array.
How about using ES6 template literals?
var a = "cat";
var b = "fat";
console.log(`my ${a} is ${b}`); //notice back-ticked string
More about template literals...
Currently there is still no native solution in Javascript for this behavior. Tagged templates are something related, but don't solve it.
Here there is a refactor of alex's solution with an object for replacements.
The solution uses arrow functions and a similar syntax for the placeholders as the native Javascript interpolation in template literals ({} instead of %%). Also there is no need to include delimiters (%) in the names of the replacements.
There are two flavors (three with the update): descriptive, reduced, elegant reduced with groups.
Descriptive solution:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(
/{\w+}/g,
placeholderWithDelimiters => {
const placeholderWithoutDelimiters = placeholderWithDelimiters.substring(
1,
placeholderWithDelimiters.length - 1,
);
const stringReplacement = replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters;
return stringReplacement;
},
);
console.log(string);
Reduced solution:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(/{\w+}/g, placeholder =>
replacements[placeholder.substring(1, placeholder.length - 1)] || placeholder
);
console.log(string);
UPDATE 2020-12-10
Elegant reduced solution with groups, as suggested by #Kade in the comments:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(
/{(\w+)}/g,
(placeholderWithDelimiters, placeholderWithoutDelimiters) =>
replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters
);
console.log(string);
UPDATE 2021-01-21
Support empty string as a replacement, as suggested by #Jesper in the comments:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '',
};
const string = stringWithPlaceholders.replace(
/{(\w+)}/g,
(placeholderWithDelimiters, placeholderWithoutDelimiters) =>
replacements.hasOwnProperty(placeholderWithoutDelimiters) ?
replacements[placeholderWithoutDelimiters] : placeholderWithDelimiters
);
console.log(string);
You can use JQuery(jquery.validate.js) to make it work easily.
$.validator.format("My name is {0}, I'm {1} years old",["Bob","23"]);
Or if you want to use just that feature you can define that function and just use it like
function format(source, params) {
$.each(params,function (i, n) {
source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
})
return source;
}
alert(format("{0} is a {1}", ["Michael", "Guy"]));
credit to jquery.validate.js team
As with modern browser, placeholder is supported by new version of Chrome / Firefox, similar as the C style function printf().
Placeholders:
%s String.
%d,%i Integer number.
%f Floating point number.
%o Object hyperlink.
e.g.
console.log("generation 0:\t%f, %f, %f", a1a1, a1a2, a2a2);
BTW, to see the output:
In Chrome, use shortcut Ctrl + Shift + J or F12 to open developer tool.
In Firefox, use shortcut Ctrl + Shift + K or F12 to open developer tool.
#Update - nodejs support
Seems nodejs don't support %f, instead, could use %d in nodejs.
With %d number will be printed as floating number, not just integer.
Just use replace()
var values = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};
var substitutedString = "My Name is %NAME% and my age is %AGE%.".replace("%NAME%", $values["%NAME%"]).replace("%AGE%", $values["%AGE%"]);
You can use a custom replace function like this:
var str = "My Name is %NAME% and my age is %AGE%.";
var replaceData = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};
function substitute(str, data) {
var output = str.replace(/%[^%]+%/g, function(match) {
if (match in data) {
return(data[match]);
} else {
return("");
}
});
return(output);
}
var output = substitute(str, replaceData);
You can see it work here: http://jsfiddle.net/jfriend00/DyCwk/.
If you want to do something closer to console.log like replacing %s placeholders like in
>console.log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>Hello Loreto how are you today is everything allright?
I wrote this
function log() {
var args = Array.prototype.slice.call(arguments);
var rep= args.slice(1, args.length);
var i=0;
var output = args[0].replace(/%s/g, function(match,idx) {
var subst=rep.slice(i, ++i);
return( subst );
});
return(output);
}
res=log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright");
document.getElementById("console").innerHTML=res;
<span id="console"/>
you will get
>log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>"Hello Loreto how are you today is everything allright?"
UPDATE
I have added a simple variant as String.prototype useful when dealing with string transformations, here is it:
String.prototype.log = function() {
var args = Array.prototype.slice.call(arguments);
var rep= args.slice(0, args.length);
var i=0;
var output = this.replace(/%s|%d|%f|%#/g, function(match,idx) {
var subst=rep.slice(i, ++i);
return( subst );
});
return output;
}
In that case you will do
"Hello %s how are you %s is everything %s?".log("Loreto", "today", "allright")
"Hello Loreto how are you today is everything allright?"
Try this version here
This allows you to do exactly that
NPM: https://www.npmjs.com/package/stringinject
GitHub: https://github.com/tjcafferkey/stringinject
By doing the following:
var str = stringInject("My username is {username} on {platform}", { username: "tjcafferkey", platform: "GitHub" });
// My username is tjcafferkey on Git
I have written a code that lets you format string easily.
Use this function.
function format() {
if (arguments.length === 0) {
throw "No arguments";
}
const string = arguments[0];
const lst = string.split("{}");
if (lst.length !== arguments.length) {
throw "Placeholder format mismatched";
}
let string2 = "";
let off = 1;
for (let i = 0; i < lst.length; i++) {
if (off < arguments.length) {
string2 += lst[i] + arguments[off++]
} else {
string2 += lst[i]
}
}
return string2;
}
Example
format('My Name is {} and my age is {}', 'Mike', 26);
Output
My Name is Mike and my age is 26
Another solution if you're using node.js is StackExchange's own formatUnicorn utility function (https://www.npmjs.com/package/format-unicorn):
let x = {name:'jason', food:'pine cones'};
let s = '{name} enjoys a delicious bowl of {food}';
let formatted = x.formatUnicorn(s);
Also, a bit of an edge case, but if you aren't using Node but you do just happen to be writing a userscript for SE sites, then formatUnicorn will already be on the String prototype.
As a quick example:
var name = 'jack';
var age = 40;
console.log('%s is %d yrs old',name,age);
The output is:
jack is 40 yrs old
Here is another way of doing this by using es6 template literals dynamically at runtime.
const str = 'My name is ${name} and my age is ${age}.'
const obj = {name:'Simon', age:'33'}
const result = new Function('const {' + Object.keys(obj).join(',') + '} = this.obj;return `' + str + '`').call({obj})
document.body.innerHTML = result
const stringInject = (str = '', obj = {}) => {
let newStr = str;
Object.keys(obj).forEach((key) => {
let placeHolder = `#${key}#`;
if(newStr.includes(placeHolder)) {
newStr = newStr.replace(placeHolder, obj[key] || " ");
}
});
return newStr;
}
Input: stringInject("Hi #name#, How are you?", {name: "Ram"});
Output: "Hi Ram, How are you?"
ES6:
const strFormat = (str, ...args) => args.reduce((s, v) => s.replace('%s', v), str);
// Use it like:
const result = strFormat('%s is %s yrs old', 'name', 23);
Lots of good/similar answers here. I wanted the ability to easily get a nested key in an object (or perhaps some JSON data structure) for substitution, so I took the following simple approach:
const getKey = (d, path) => {
// path can be a string like 'key1.key2' or an iterable of keys
if (typeof(path) === 'string') {
path = path.split('.')
}
return path.reduce((x, y) => x[y], d)
}
const inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> getKey(obj, g));
// Example
> const str = 'there are ${a} ways to ${b.c}'
undefined
> inject(str, {'a':'many', 'b': {'c': 'skin a cat'}})
'there are many ways to skin a cat'
Some inspiration from this and this.
This is a merged solution of Gerson Diniz and Shubham Vyas.
It is possible to pass a set of arguments or an object.
function strSwap(str) {
if (!str) return null;
let args = [];
for (let a of arguments)
args.push(a);
args.shift();
if (!args.length) return null;
// replacement by object - {{prop}}
if (!!(args[0].constructor && args[0].constructor.name.toLowerCase() === 'object')) {
for (let i in args[0]) {
let n = `{{${i}}}`;
str = str.includes(n) ? str.replaceAll(n, args[0][i] + '') : str;
}
}
// replacement by placeholders - %s
else {
str = args.reduce((s, v) => s.replace('%s', v), str);
}
return str;
}
// ---------------------
console.log(strSwap('Hello %s, my name is %s.', 'alice', 'bob'));
console.log(strSwap('Hello {{a}}, my name is {{b}}. Hello {{b}}.', {
a: 'alice',
b: 'bob'
}));

Array of String Search in express.js

I have array of string like this
let search = ["user","country"];
I want to get data from mysql database using LIKE operator.
For example like this
searchStmnt = `u.u_fullname LIKE "%` + search + `%"`
But the above code is not working. Can anyone suggest me how to do that?
const search = ["user", "country"];
const searchStmnt = search.map(item => `u.u_fullname LIKE '%${item}%'`).join(" OR ");
console.log(searchStmnt);
...or with REGEXP:
const search = ["user", "country"];
const searchStmnt = `u.u_fullname REGEXP '(${search.join("|")})'`;
console.log(searchStmnt);

Replace = with : in serialized data

i'm recieving a serialzed data in the form as
error_message=%7B%0D%0A+null+%7D&systemId=53183&cert-id=176061&score=0&q2c=3%5C0&q2t=&answer_data=0000
how can i replace the = with : using typescript and replace should be for all the occurence of = except first that is error_message=value&systemId:value&cert-id=value and so on
i was trying with splice but it seems to be a long process and not that fast as the string grows.
You can do that by splitting the string by = and then treating the first element different from all others:
const input = 'error_message=%7B%0D%0A+null+%7D&systemId=53183&cert-id=176061&score=0&q2c=3%5C0&q2t=&answer_data=0000';
const repl = (inp) => {
const [firstEl, ...rest] = inp.split(/\=/); // Split by '='
return `${firstEl}=${rest.join(':')}`; // <first el> + '=' + <all other elements joined by ':'>
}
console.log(repl(input));
It looks like these are URL search query parameters, so you can get them in a more manageable format using the URLSearchParams constructor, then manipulate them however you want:
const queryString = 'error_message=%7B%0D%0A+null+%7D&systemId=53183&cert-id=176061&score=0&q2c=3%5C0&q2t=&answer_data=0000'
const [ first, ...rest ] = [...new URLSearchParams(queryString)]
.map(([k, v]) => [k, v].map(encodeURIComponent))
// assuming you still want URI encoding, else remove mapping
const result = [
first.join(':'),
...rest.map(x => x.join('=')),
].join('&')
console.log(result)
You can get the first '=' with the method indexOf(), then slice the string into the two different parts (one before the first '=' and one after) and replace the following equal signs
const string = "error_message=%7B%0D%0A+null+%7D&systemId=53183&cert-id=176061&score=0&q2c=3%5C0&q2t=&answer_data=0000"
const firstSignIndex = string.indexOf('=')
const result = string.slice(0, firstSignIndex+1) + string.slice(firstSignIndex+1).replace(/=/g, ':')
console.log(result)
Check this out, this will help you to replace the = with : except the first occurrence of = in your serialized string.
let a = 'error_message=%7B%0D%0A+null+%7D&systemId=53183&cert-id=176061&score=0&q2c=3%5C0&q2t=&answer_data=0000',
firstOcc = a.indexOf("error_message=") + 'error_message='.length,
starterStr = a.substring(0, firstOcc),
replacedStr = a.substring(firstOcc, a.length).replace(/=/g, ':');
console.log(starterStr + replacedStr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You can make changes based on your needs firstOcc = a.indexOf("error_message=") + 'error_message='.length,

Merge and create an unique case sensitive array in javascript

It's a complicated scenario but will try to explain as much as possible. I have one object or arrays and one array. Now i have to compare selectedmodel values with mappedmodels, if the value(Case insensitive) matches with that object then fetch all the values of it and push it into selected model and combine both. Hope the example will clear what i am trying to achive.
var mappedModels = { 'CC605': ['cc605', 'CC605', 'cC605'], 'TC75X': ['TC75X'] };
var selectedModels = ['CC605', 'tc76'];
var desiredOutput = ["CC605", "tc76", "cc605", "cC605"];
I already wrote a solution to achieve it but i need a better code in terms of performance. Here is the solution:
function combineModelCases(selectedModels) {
const modelCases = [];
selectedModels.forEach(elm => {
const existingModels = mappedModels[elm.toUpperCase()];
if (existingModels) {
for (const key of existingModels) {
if (elm.toUpperCase() !== key) {
modelCases.push(key);
}
}
}
});
return selectedModels.concat(modelCases);
}
Here is Fiddle
I am using typescript and underscore js for your references. Any help would be very helpful.
You could use flatMap to get a flattened array of values for each key in selectedModels. Then, create a Set to get a unique collection models. Use Array.from() to convert the set to an array.
const mappedModels = { 'CC605': ['cc605', 'CC605', 'cC605'], 'TC75X': ['TC75X'] },
selectedModels = ['CC605', 'tc76'];
const models = selectedModels.flatMap(m => mappedModels[m] || []),
unique = Array.from(new Set([...selectedModels, ...models]));
console.log(unique)
you can do the following, :
var mappedModels = { 'CC605': ['cc605', 'CC605', 'cC605'], 'TC75X': ['TC75X'] };
var selectedModels = ['CC605', 'tc76'];
var desiredOutput;
function combineModelCases(selectedValue){
if(mappedModels.hasOwnProperty(selectedValue)){
desiredOutput = [... new Set([...mappedModels[selectedValue], ...selectedModels])]
return desiredOutput;
}
}
console.log(combineModelCases('CC605'));
here is the working demo : https://jsfiddle.net/wzo4d6uy/2/:

How to create a list object from outerHTML value of a table?

please check out this fiddle.
This has an outerHTML var. Now, I have to create it as a list of objects ; objects will be each row.
In this example the row number is 3 , but I will get it in a var like
var maxrow = $('#rownumber').val();
var TotalObject = [] ;
that will contain :
{
{column1header :row1col1value ,column2header :row1col2value ,column3header :row1col3value,column4header :row1col4value },
{column1header :row2col1value ,column2header :row2col2value ,column3header :row2col3value,column4header :row2col4value },
{column1header :row2col1value ,column2header :row2col2value ,column3header :row2col3value,column4header :row2col4value }
}
Then, I have to send this list of objects to my controller action method with an ajax call :
[httppost]
public PartialViewResult MyAction(list<mymodel> model)
{
return Partialview ("mypartialview",model);
}
mymodel also has IEnumerable<mymodel> where I have to transform this to that.
How about this approach :
Clean the input with a regular expression
Split it into an array
Create an iterator for your data
Push the result into a JSON object
Send the JSON result with Ajax?
EDIT: I improved the cleanup regular expressions ; it should account for empty values now.
It is based on the HTML markup, which isn't valid in your example (missing </tr> or <tr> tags).
var outerhtml = "\r\n\r\n<thead>\r\n\r\n<tr>\r\n\r\n<th>\r\n\r\ncolumn1header\r\n\r\n\r\n\r\n</th>\r\n\r\n<th>\r\n\r\ncolumn2header\r\n\r\n</th>\r\n\r\n<th>\r\n\r\ncolumn3header\r\n\r\n</th>\r\n\r\n<th>\r\n\r\ncolumn4header\r\n\r\n</th>\r\n\r\n</tr>\r\n\r\n</thead>\r\n\r\n<tbody>\r\n\r\n<tr>\r\n\r\n<td>\r\n\r\nrow1col1value\r\n\r\n</td>\r\n\r\n<td>\r\n\r\nrow1col2value\r\n\r\n</td>\r\n\r\n<td>\r\n\r\nrow1col3value\r\n\r\n</td>\r\n\r\n<td>\r\n\r\nrow1col1value\r\n\r\n</td>\r\n\r\n</tr>\r\n\r\n<tr>\r\n\r\n<td>\r\n\r\nrow2col1value\r\n\r\n</td>\r\n\r\n<td>\r\n\r\nrow2col2value\r\n\r\n</td>\r\n\r\n<td>\r\n\r\nrow2col3value</td>\r\n\r\n<td>\r\n\r\nrow2col4value\r\n\r\n</td>\r\n\r\n</tr>\r\n\r\n<td>\r\n\r\nrow3col1value\r\n\r\n</td>\r\n\r\n<td>\r\n\r\n row3col2value\r\n\r\n </td>\r\n\r\n<td>\r\n\r\nrow3col3value\r\n\r\n</td>\r\n\r\n <td>\r\n\r\n row3col4value \r\n\r\n </td>\r\n\r\n</tbody>\r\n\r\n";
const data = outerhtml.split("\r\n").join("");
const [_,h,v] = /(?:<thead><tr>(.*)<\/tr><\/thead>)(?:<tbody>(.*)<\/tbody>)/igm.exec(data);
const headers = h.split("</th><th>")
.map(t => t.replace("<th>","")
.replace("</th>",""));
const values = v.match(/(?:<td>\s*(\s*\w*)\s*<\/td>)/igm)
.map(t => t.replace("<td>","")
.replace("</td>","")
.trim());
const iterator = function(array){
const coll = [...array];
return {
[Symbol.iterator](){
let nextIndex = 0;
return {
next: function(){
const _r = {
[headers[0]] : coll[nextIndex],
[headers[1]] : coll[nextIndex+1],
[headers[2]] : coll[nextIndex+2],
[headers[3]] : coll[nextIndex+3]
};
nextIndex += 4;
return nextIndex <= coll.length ?
{value: _r, done: false} :
{done: true};
}
}
}
}
}
let json=[];
const tuples = iterator(values);
for(tuple of tuples){
json.push(tuple);
}
console.log(json);

Categories