JS replace substring with keys in object - javascript

I am developing a general function inside a solution which would replace /endpoint/{item.id}/disable/{item.name} with /endpoint/123/disable/lorem where I pass the function the URL and item.
The item would be an object with keys id and name.
What would be the best way to find items with the structure {item.KEY} and replacing them with item.KEY?

the best way to solve this is passing a function to handle a regex.
I modified your parameters to make the mapping easier- I'm sure you can change this on your own if necessary
var url = '/endpoint/{id}/disable/{name}'; //I modified your parameters
var definition = { id: 123, name: 'lorem' };
url = url.replace(/{([^}]*)}/g, function (prop) {
var key = prop.substr(1, prop.length - 2);
if (definition[key])
return encodeURIComponent(definition[key]);
else
throw new Error('missing required property "' + key + '" in property collection');
});
//alert(url);
fiddle: https://jsfiddle.net/wovr4ct5/2/

If you don't want to use eval, maybe use something like this:
var item = {
id: 123,
KEY: "lorem"
};
function pick(o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
var a = s.split('.');
for (var i = 0, n = a.length; i < n; ++i) {
var k = a[i];
if (k in o) {
o = o[k];
} else {
return;
}
}
return o;
}
and then
str.replace(/{.*?}/g, function(match){ // match {...}
var path = match.substring(1,match.length-1); // get rid of brackets.
return pick(scope, path); //get value and replace it in string.
});

Related

What's the best way to convert cookie string to an object [duplicate]

I have a string similiar to document.cookie:
var str = 'foo=bar, baz=quux';
Converting it into an array is very easy:
str = str.split(', ');
for (var i = 0; i < str.length; i++) {
str[i].split('=');
}
It produces something like this:
[['foo', 'bar'], ['baz', 'quux']]
Converting to an object (which would be more appropriate in this case) is harder.
str = JSON.parse('{' + str.replace('=', ':') + '}');
This produces an object like this, which is invalid:
{foo: bar, baz: quux}
I want an object like this:
{'foo': 'bar', 'baz': 'quux'}
Note: I've used single quotes in my examples, but when posting your code, if you're using JSON.parse(), keep in your mind that it requires double quotes instead of single.
Update
Thanks for everybody. Here's the function I'll use (for future reference):
function str_obj(str) {
str = str.split(', ');
var result = {};
for (var i = 0; i < str.length; i++) {
var cur = str[i].split('=');
result[cur[0]] = cur[1];
}
return result;
}
The shortest way
document.cookie.split('; ').reduce((prev, current) => {
const [name, ...value] = current.split('=');
prev[name] = value.join('=');
return prev;
}, {});
Why exactly do you need JSON.parse in here? Modifying your arrays example
let str = "foo=bar; baz=quux";
str = str.split('; ');
const result = {};
for (let i in str) {
const cur = str[i].split('=');
result[cur[0]] = cur[1];
}
console.log(result);
note : The document.cookie (question headline) is semicolon separated and not comma separated (question) ...
An alternative using reduce :
var str = 'foo=bar; baz=quux';
var obj = str.split(/[;] */).reduce(function(result, pairStr) {
var arr = pairStr.split('=');
if (arr.length === 2) { result[arr[0]] = arr[1]; }
return result;
}, {});
A way to parse cookies using native methods like URLSearchParams and Object.fromEntries, avoiding loops and temporary variables.
Parsing document.cookie:
Object.fromEntries(new URLSearchParams(document.cookie.replace(/; /g, "&")))
For the scope of the question (cookies are separated by , and stored in variable str)
Object.fromEntries(new URLSearchParams(str.replace(/, /g, "&")))
Given an array a containing your intermediate form:
[['foo', 'bar'], ['baz', 'quux']]
then simply:
var obj = {};
for (var i = 0; i < a.length; ++i) {
var tmp = a[i];
obj[tmp[0]] = tmp[1];
}
To convert it to an object, just do that from the beginning:
var obj = {};
str = str.split(', ');
for (var i = 0; i < str.length; i++) {
var tmp = str[i].split('=');
obj[tmp[0]] = tmp[1];
}
Then, if you want JSON out of it:
var jsonString = JSON.stringify(obj);
parse cookies (IE9+):
document.cookie.split('; ').reduce((result, v) => {
const k = v.split('=');
result[k[0]] = k[1];
return result;
}, {})
I'm a fan of John Resig's "Search and don't replace" method for this sort of thing:
var str = 'foo=bar, baz=quux',
arr = [],
res = '{';
str.replace(/([^\s,=]+)=([^,]+)(?=,|$)/g, function ($0, key, value) {
arr.push('"' + key + '":"' + value + '"');
});
res += arr.join(",") + "}";
alert(res);
Working example: http://jsfiddle.net/cm6MT/.
Makes things a lot simpler without the need for JSON support. Of course, it's just as easy to use the same regular expression with exec() or match().
Whoops, I thought you wanted to convert to a JSON string, not an object. In that case, you only need to modify the code slightly:
var str = 'foo=bar, baz=quux',
res = {};
str.replace(/([^\s,=]+)=([^,]+)(?=,|$)/g, function ($0, key, value) {
res[key] = value;
});
console.log(res.foo);
//-> "bar"
Working example 2: http://jsfiddle.net/cm6MT/1/
Most of the above solutions fail with the __gads cookie that Google sets because it uses a '=' character in the cookie value.
The solution is to use a regular expression instead of calling split('='):
document.cookie.split(';').reduce((prev, current) => {
const [name, value] = current.split(/\s?(.*?)=(.*)/).splice(1, 2);
prev[name] = value;
return prev;
}, {});
That's pretty crappy data, as long as its not using ,= this would work on that data
var text = 'foo=bar, baz=quux',
pattern = new RegExp(/\b([^=,]+)=([^=,]+)\b/g),
obj = {};
while (match = pattern.exec(text)) obj[match[1]] = match[2];
console.dir(obj);
An alternate version of your updated solution that checks for the null/empty string and just returns an empty object and also allows for custom delimiters.
function stringToObject(str, delimiter) {
var result = {};
if (str && str.length > 0) {
str = str.split(delimiter || ',');
for (var i = 0; i < str.length; i++) {
var cur = str[i].split('=');
result[cur[0]] = cur[1];
}
}
return result;
}
first thing that occurred to me, I'll leave it as the original version, but cookies should not be empty otherwise there will be a json parse error
JSON.parse(`{"${document.cookie.replace(/=/g,'":"').replace(/; /g,'","')}"}`)
fast and reliable version - cookie to object
let c=document.cookie.split('; '),i=c.length,o={};
while(i--){let a=c[i].split('=');o[a[0]]=a[1]}
and short function for get single cookie
getCookie=e=>(e=document.cookie.match(e+'=([^;]+)'),e&&e[1])
function getCookie(){
var o=document.cookie.split("; ");
var r=[{}];
for(var i=0;i<o.length;i++){
r[o[i].split("=")[0]] = o[i].split("=")[1];
}
return r;
}
Just call getCookie() and it will return all cookies from the current website.
If you have a cookie called 'mycookie' you can run getCookie()['mycookie']; and it will return the value of the cookie 'mycookie'.
There is also a One-Line option:
function getCookie(){var o=document.cookie.split("; ");var r=[{}];for(var i=0;i<o.length;i++){r[o[i].split("=")[0]] = o[i].split("=")[1];}return r;}
This one can be used with the same methods as above.

Serialize/Reg ex

I use serialize function to get all the values of my form.
My problem is how can i extract only the value in the generated url string of the serialize function and split it into an array.
var input = $('#addForm').serialize();
deviceBrand=itemBrand&deviceModel=itemModel&deviceSerial=itemSerial&deviceType=Desktop&deviceStatus=Available&deviceDesc=item+description //generated url string
i need string function to extract each string between "=" and "&" and put each String into an array.
Don't use serialize, use serializeArray. It creates an array of objects of the form {name: "xxx", value: "yyy" } that you can process more easily.
var input = $("#addForm").serializeArray();
var values = input.map(x => x.value);
You can also make an object with all the name: value properties:
var object = {};
input.map(x => object[x.name] = x.value);
try this code to unserialize the string,
(function($){
$.unserialize = function(serializedString){
var str = decodeURI(serializedString);
var pairs = str.split('&');
var obj = {}, p, idx, val;
for (var i=0, n=pairs.length; i < n; i++) {
p = pairs[i].split('=');
idx = p[0];
if (idx.indexOf("[]") == (idx.length - 2)) {
// Eh um vetor
var ind = idx.substring(0, idx.length-2)
if (obj[ind] === undefined) {
obj[ind] = [];
}
obj[ind].push(p[1]);
}
else {
obj[idx] = p[1];
}
}
return obj;
};
})(jQuery);

two arrays, group second array based on repetitive values of first and form a string

I have two arrays and a string as shown below
var str=offer?;
var names = [channelId, channelId, offerType, offerType, Language];
var values =[647, 763, international, programming, English];
Both arrays will be of same size.
I need to form a string such that
final string = offer?channelId=647,763&offerType=international,programming&language=English
I need this to be done in JavaScript.
I tried it this was:
var namesMatched=false;
for(var i=0; i<names.lengthl; i++){
for(var j=i+1; j<names.length; j++){
if(names[i]==names[j]){
str=str+names[i]+"="+values[i]+","+values[j];
namesMatched=true;
continue;
}
else if(namesMatched){
str=str+"&";
i=names.length-j;
}
else{
str=str+names[i]+"="+values[i]+"&";
break;
}
}
}
But i'm not getting as expected.
Any help here is appreciated.
Here's my solution, you don't need two for sentences:
var str='offer?';
var names = ['channelId', 'channelId', 'offerType', 'offerType', 'Language'];
var values =[647, 763, 'international', 'programming', 'English'];
var last = '';
for (var i=0; i<names.length; i++) {
var name = names[i];
if (last === name) {
str+=','+values[i];
} else {
if (i !== 0) {
str+='&';
}
str+=name+'='+values[i];
}
last = name;
}
console.log(str);
http://jsbin.com/kinefipalu/edit?html,js,output
You can use (explanatory comments inline)
var str='offer?';
var names = ['channelId', 'channelId', 'offerType', 'offerType', 'Language'];
var values = [647, 763, 'international', 'programming', 'English'];
var temp = names.reduce(function (a, b, i) {
// add an array object for each name if it doesn't alreay exist
a[names[i]] = (a[names[i]] || [])
// push the value onto the arra
a[names[i]].push(values[i])
return a;
}, {})
// loopt through the properties of the temporary object (a collection of value arrays tagged by name)
var output = str + Object.keys(temp).map(function (e) {
// construct the substring for each key
return e + '=' + temp[e].join(',');
// join the substrings using &
}).join('&')
console.log(output)
Steps
You first use reduce https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce to consolidate the 2 arrays into a single object (temp). Each unique name will be a property on the object and the value will be an array of values that are tagged to that name.
You use Object.keys() to get all the keys (names) in the temporary object https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
You use map to convert the names and their corresponding value arrays to a substring https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map
You then use join https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join to join all these substrings together
var str='offer?';
var names = ['channelId', 'channelId', 'offerType', 'offerType', 'Language'];
var values = [647, 763, 'international', 'programming', 'English'];
var temp = names.reduce(function (a, b, i) {
// add an array object for each name if it doesn't alreay exist
a[names[i]] = (a[names[i]] || [])
// push the value onto the arra
a[names[i]].push(values[i])
return a;
}, {})
// loopt through the properties of the temporary object (a collection of value arrays tagged by name)
var output = str + Object.keys(temp).map(function (e) {
// construct the substring for each key
return e + '=' + temp[e].join(',');
// join the substrings using &
}).join('&')
alert(output)
If you're OK with using Underscore, you can group the values with
groups = _.groupBy(values, function(val, i) { return names[i]; });
In other words, group the values by the name found in the corresponding position in the names array. That will give you
{ channelId: [647, 763], ... }
You can turn that into your final string easily enough with
_.map(groups, function(val, key) { return key + '=' + val; }) . join('&');
Note in the above line that val, which is an array, when coerced to a string automatically joins its elements with a comma.
Or, depending on your preferred programming style:
function name(val, i) { return names[i]; }
function key(val, key) { return key + '=' + val; }
_.map(_.groupBy(values, name), key) . join('&')
or with chaining
_.chain(values) . groupBy(name) . map(key) . join('&') . value()

Find and update a hash by HashKey to add record for saving

Lets say I have an array of hashes:
hash = [{"one": 1}, {"two": 2}]
And I want to find a hash and add to it. For example find "one" and add:
hash = [{"one": 1, "new": new}, {"two": 2}]
I could do this by hash key? If so how would I do it? Or is there a much better way to do this thing in Javascript? I dont want to copy the hash, make a new one and delete the old one. Just update what is already there.
JavaScript is pretty dynamic so you should be able to do something like this:
var hash = [{"one": 1}, {"two": 2}];
var hLength = hash.length;
for(int i=0; i<hLength; i++){ // Loop to find the required object.
var obj = hash[i];
if(obj.hasOwnProperty('one')){ // Condition you're looking for
obj["new"] = "new"; // Property you wish to add.
break;
}
}
If you're happy to use underscore you could get it done by starting with:
var hashes = [{"one": 1}, {"two": 2}];
var changed = _.map(hashes, function(hash){
if(hash.one) {
hash["new"] = "new";
return hash
}
return hash;
});
You could generalise it a bit by passing a filter function to encapsulate the if statement, and another function to encapsulate the modification to the hash.
EDIT If you want to generalise what to look for in the hash, this could work:
var hashes = [{"one": 1}, {"two": 2}];
var isOne = function(hash) {
return hash.one;
}
var addNew = function(hash) {
hash["new"] = "new";
return hash;
}
var hashChanger = function(filter, editor) {
return function(hash) {
if(filter(hash)) {
return editor(hash);
}
return hash;
}
}
var changed = _.map(hashes, hashChanger(isOne, addNew));
Here is a function I just wrote to do this.
/*
* hashes - (array) of hashes
* needle - (string) key to search for / (int) index of object
* key - (string) key of new object you wish to insert
* value - (mixed) value of new object you wish to insert
*/
function addToHash(hashes, needle, key, value) {
var count = hashes.length;
// If needle is a number treat it as an array key
if (typeof needle === 'number' && needle < count) {
hashes[needle][key] = value;
return true;
} else {
// Search hashes for needle
for (var i=0; i<count; i++)
{
if (needle in hashes[i]) {
hashes[i][key] = value;
return true;
}
}
}
return false;
}

String to object in JS

I have a string as
string = "firstName:name1, lastName:last1";
now I need one object obj such that
obj = {firstName:name1, lastName:last1}
How can I do this in JS?
Actually, the best solution is using JSON:
Documentation
JSON.parse(text[, reviver]);
Examples:
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)
Passing a function to 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();
Your string looks like a JSON string without the curly braces.
This should work then:
obj = eval('({' + str + '})');
WARNING: this introduces significant security holes such as XSS with untrusted data (data that is entered by the users of your application.)
If I'm understanding correctly:
var properties = string.split(', ');
var obj = {};
properties.forEach(function(property) {
var tup = property.split(':');
obj[tup[0]] = tup[1];
});
I'm assuming that the property name is to the left of the colon and the string value that it takes on is to the right.
Note that Array.forEach is JavaScript 1.6 -- you may want to use a toolkit for maximum compatibility.
This simple way...
var string = "{firstName:'name1', lastName:'last1'}";
eval('var obj='+string);
alert(obj.firstName);
output
name1
Since JSON.parse() method requires the Object keys to be enclosed within quotes for it to work correctly, we would first have to convert the string into a JSON formatted string before calling JSON.parse() method.
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
This would work even if the string has a complex object (like the following) and it would still convert correctly. Just make sure that the string itself is enclosed within single quotes.
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
If you have a string like foo: 1, bar: 2 you can convert it to a valid obj with:
str
.split(',')
.map(x => x.split(':').map(y => y.trim()))
.reduce((a, x) => {
a[x[0]] = x[1];
return a;
}, {});
Thanks to niggler in #javascript for that.
Update with explanations:
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)
Confusing MDN docs:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Demo: http://jsbin.com/hiduhijevu/edit?js,console
Function:
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!
You need use JSON.parse() for convert String into a Object:
var obj = JSON.parse('{ "firstName":"name1", "lastName": "last1" }');
if you're using JQuery:
var obj = jQuery.parseJSON('{"path":"/img/filename.jpg"}');
console.log(obj.path); // will print /img/filename.jpg
REMEMBER: eval is evil! :D
I implemented a solution in a few lines of code which works quite reliably.
Having an HTML element like this where I want to pass custom options:
<div class="my-element"
data-options="background-color: #dadada; custom-key: custom-value;">
</div>
a function parses the custom options and return an object to use that somewhere:
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')));
In your case, The short and beautiful code
Object.fromEntries(str.split(',').map(i => i.split(':')));
I'm using JSON5, and it's works pretty well.
The good part is it contains no eval and no new Function, very safe to use.
string = "firstName:name1, lastName:last1";
This will work:
var fields = string.split(', '),
fieldObject = {};
if( typeof fields === 'object') ){
fields.each(function(field) {
var c = property.split(':');
fieldObject[c[0]] = c[1];
});
}
However it's not efficient. What happens when you have something like this:
string = "firstName:name1, lastName:last1, profileUrl:http://localhost/site/profile/1";
split() will split 'http'. So i suggest you use a special delimiter like pipe
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];
});
}
const text = '{"name":"John", "age":30, "city":"New York"}';
const myArr = JSON.parse(text);
document.getElementById("demo").innerHTML = myArr.name;
This is universal code , no matter how your input is long but in same schema if there is : separator :)
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)
Here is my approach to handle some edge cases like having whitespaces and other primitive types as values
const str = " c:234 , d:sdfg ,e: true, f:null, g: undefined, h:name ";
const strToObj = str
.trim()
.split(",")
.reduce((acc, item) => {
const [key, val = ""] = item.trim().split(":");
let newVal = val.trim();
if (newVal == "null") {
newVal = null;
} else if (newVal == "undefined") {
newVal = void 0;
} else if (!Number.isNaN(Number(newVal))) {
newVal = Number(newVal);
}else if (newVal == "true" || newVal == "false") {
newVal = Boolean(newVal);
}
return { ...acc, [key.trim()]: newVal };
}, {});
In your case
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]);
}
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);
});
"objects" array will have all the objects
I know this is an old post but didn't see the correct answer for the question.
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);
Now you can use obj.firstName and obj.lastName to get the values as you could do normally with an object.
You don't have to always convert to JSON
So here "person begin as a string!" Finally, "person is converted to object", no necessarily to JSON.
function strToObj(e){if(typeof e=="string"){ let obj=new Function("return" +e); try{return obj()}catch{console.log("Fix, String no allowed to object")}}else{console.log("it is not a string") } };
//Example, person is a string
let person='{firstName:"John", lastName:"Doe", id: 55, fullName:function(){return this.firstName+" "+this.lastName} }';
console.log(strToObj(person));
And it run functions internal to the object without major issues if it is called:
person=strToObj(person); console.log(person.fullName())
Simply, string = "firstName:name1, lastName:last1";
let string = "firstName:name1, lastName:last1";
let object= strToObj("{"+string+"}");
console.log(object)

Categories

Resources