How to extract values from a string in javascript? - javascript

I need some help with extracting values from a cookie using javascript.
The string in a cookie looks something like this:
string = 'id=1||price=500||name=Item name||shipping=0||quantity=2++id=2||price=1500||name=Some other name||shipping=10||quantity=2'
By using string.split() and string.replace() and a some ugly looking code I've somehow managed to get the values i need (price, name, shipping, quantity). But the problem is that sometimes not all of the strings in the cookie are the same. Sometimes the sting in a cookie will look something like this :
string = 'id=c1||color=red||size=XL||price=500||name=Item name||shipping=0||quantity=2++id=c1||price=500||name=Item name||shipping=0||quantity=2'
with some items having color and size as parameters and sometimes only one of those.
Is there some more efficient way to explain to my computer that i want the part of the string after 'price=' to be a variable named 'price' etc.
I hope I'm making sense I've tried to be as precise as I could.
Anyway, thank you for any help
EDIT: I just wanted to say thanks to all the great people of StackOverflow for such wonderfull ideas. Because of all of your great suggestions I'm going out to get drunk tonight. Thank you all :)

Let's write a parser!
function parse(input)
{
function parseSingle(input)
{
var parts = input.split('||'),
part,
record = {};
for (var i=0; i<parts.length; i++)
{
part = parts[i].split('=');
record[part[0]] = part[1];
}
return record;
}
var parts = input.split('++'),
records = [];
for (var i=0; i<parts.length; i++)
{
records.push(parseSingle(parts[i]));
}
return records;
}
Usage:
var string = 'id=1||price=500||name=Item name||shipping=0||quantity=2++id=2||price=1500||name=Some other name||shipping=10||quantity=2';
var parsed = parse(string);
/* parsed is:
[{id: "1", price: "500", name: "Item name", shipping: "0", quantity: "2"},
{id: "2", price: "1500", name: "Some other name", shipping: "10", quantity: "2"}]
*/

You can achieve this using regular expressions. For example, the regex /price=([0-9]+)/ will match price=XXX where XXX is one or more numbers. As this part of the regex is surrounded by parenthesis it explicitly captures the numeric part for you.
var string = 'id=1||price=500||name=Item name||shipping=0||quantity=2++id=2||price=1500||name=Some other name||shipping=10||quantity=2'
var priceRegex = /price=([0-9]+)/
var match = string.match(priceRegex);
console.log(match[1]); // writes 500 to the console log

Try that:
var string = 'id=1||price=500||name=Item name||shipping=0||quantity=2++id=2||price=1500||name=Some other name||shipping=10||quantity=2';
var obj = new Array();
var arr = string.split('||');
for(var x=0; x<arr.length;x++){
var temp = arr[x].split('=');
obj[temp[0]] = temp[1]
}
alert(obj['id']); // alert 1

First, split your string into two (or more) parts by ++ separator:
var strings = myString.split('++');
then for each of the strings you want an object, right? So you need to have an array and fill it like that:
var objects = [];
for (var i = 0; i < strings.length; ++i) {
var properties = strings[i].split('||');
var obj = {};
for (var j = 0; j < properties.length; ++j) {
var prop = properties[j].split('=');
obj[prop[0]] = prop[1]; //here you add property to your object, no matter what its name is
}
objects.push(obj);
}
thus you have an array of all objects constructed from your string. Naturally, in real life I'd add some checks that strings indeed satisfy the format etc. But the idea is clear, I hope.

If you can replace the || with &, you could try to parse it as if it were a query string.
A personal note - JSON-formatted data would've been easier to work with.

I would attach the data to a javascript object.
var settingsObj = {};
var components = thatString.split('||');
for(var j = 0; j < components.length; j++)
{
var keyValue = components[j].split('=');
settingsObj[keyValue[0]] = keyValue[1];
}
// Now the key value pairs have been set, you can simply request them
var id = settingsObj.id; // 1 or c1
var name = settingsObj.name; // Item Name, etc

You're already using .split() to break down the string by || just take that a step further and split each of those sections by = and assign everything on the left the field and the right the value

This should get the first match in the string:
string.match(/price=(\d{1,})/)[1]
Note this will only match the first price= in the string, not the second one.

If you can use jQuery, it wraps working with cookies and lets you access them like:
Reading a cookie:
var comments = $.cookie('comments');
Writing a cookie:
$.cookie('comments', 'expanded');
This post by someone else has a decent example:
http://www.vagrantradio.com/2009/10/getting-and-setting-cookies-with-jquery.html
If you can't use jQuery, you need to do standard string parsing like you currently are (perhaps regular expressions instead of the string splitting / replacing might trim down your code) or find some other javascript library that you can use.

If you like eye candies in your code you can use a regexp based "search and don't replace" trick by John Resig (cached here) :
var extract = function(string) {
var o = {};
string.replace(/(.*?)=(.*?)(?:\|\||$)/g, function(all, key, value) {
o[key] = value;
});
return o;
};
Then
var objects = string.split('++'),
i = objects.length;
for (;i--;) {
objects[i] = extract(objects[i]);
}

You could do something like this, where you eval the strings when you split them.
<html>
<head>
<script type="text/javascript">
var string = 'id=c1||color=red||size=XL||price=500||name=Item name||shipping=0||quantity=2++id=c1||price=500||name=Item name||shipping=0||quantity=2'
var mySplitResult = string.split("||");
for(i = 0; i < mySplitResult.length; i++){
document.write("<br /> Element " + i + " = " + mySplitResult[i]);
var assignment = mySplitResult[i].split("=");
eval(assignment[0] + "=" + "\""+assignment[1]+"\"");
}
document.write("Price : " + price);
</script>
</head>
<body>
</body>
</html>

var str = 'id=c1||color=red||size=XL||price=500||name=Item name||shipping=0||quantity=2++id=c1||price=500||name=Item name||shipping=0||quantity=2'
var items = str.split("++");
for (var i=0; i<items.length; i++) {
var data = items[i].split("||");
for (var j=0; j<data.length; j++) {
var stuff = data[j].split("=");
var n = stuff[0];
var v = stuff[1];
eval("var "+n+"='"+v+"'");
}
alert(id);
}
EDIT: As per JamieC's suggestion, you can eliminate eval("var "+n+"='"+v+"'"); and replace it with the (somewhat) safer window[n] = v; -- but you still have the simple problem that this will overwrite existing variables, not to mention you can't tell if the variable color was set on this iteration or if this one skipped it and the last one set it. Creating an empty object before the loop and populating it inside the loop (like every other answer suggests) is a better approach in almost every way.

JSON.parse('[{' + string.replace(/\+\+/g, '},{').replace(/(\w*)=([\w\s]*)/g, '"$1":"$2"').replace(/\|\|/g, ',') + '}]')
Convert the string for JSON format, then parse it.

Related

Google scripts. TypeError: Cannot call method "replace" of undefined

First post please go easy on me.
I have an array that looks something like this [BTC-LTC, BTC-DOGE, BTC-VTC] I am trying to change all the "-" with "_". But am having trouble with using the .replace() method. Here is my code.
var array = [BTC-LTC, BTC-DOGE, BTC-VTC];
var fixedArray = [];
for(var i=0; i <= array.length; i++){
var str = JSON.stringify(array[i]);
var res = str.replace("-","_");
fixedArray.push(res);
};
I tried without using the JSON.stringify but that didn't work either. I have also tried to first create var str = String(); this also did not work. Is it possible that the method .replace() is not available in google scripts?
In your example var array = [BTC-LTC, BTC-DOGE, BTC-VTC];
should be
var array = ["BTC-LTC", "BTC-DOGE", "BTC-VTC"];
However I gather from the comments that this is just a typo in your initial example.
var str = JSON.stringify(array[i]); is redundant. You can just do var str = array[i]; Since the value in the array is already a string, there's no need to turn it into one again - the "stringify" method expects to be given an object or array to work on.
However the main problem is that your for loop goes on one too many iterations. Arrays are zero-based, so you need to stop looping when the index is 1 less than the length of the array, not equal to it. e.g. if array.length is 10 then there are 10 indices, but they start at 0, so the indices are 0,1,2,3,4,5,6,7,8,9. If your loop goes on to equal to array.length, then on the last loop array[10] will be out of bounds, and it's only this last iteration which is giving you the undefined error.
var array = ["BTC-LTC", "BTC-DOGE", "BTC-VTC"];
var fixedArray = [];
for (var i = 0; i < array.length; i++) {
var str = array[i];
var res = str.replace("-","_");
fixedArray.push(res);
}
If I understood correctly, you're trying to edit strings, not variables, so you need quotes in your array, and a g in your replace in case you have multiple things to replace :
var array = ['BTC-LTC', 'BTC-DOGE', 'BTC-VTC'];
var fixedArray = [];
for(var i=0; i <= array.length; i++){
fixedArray.push(array[i].replace(/-/g, '_'));
};
code is working fine if we change as below:
var array = ['BTC-LTC', 'BTC-DOGE', 'BTC-VTC'];

Replacing custom characters during string.split()

I have a fairly large javascript/html application that updates frequently and receives a lot of data. It's running very quickly and smoothly but I need to now introduce a function that will have to process any incoming data for special chars, and I fear it will be a lot of extra processing time (and jsperf is kinda dead at the moment).
I will make a request to get a .json file via AJAX and then simply use the data as is. But now I will need to look out for strings with #2C (hex comma) because all of the incoming data is comma-separated values.
in File.json
{
names: "Bob, Billy",
likes : "meat,potatoes
}
Now I need
{
names: "Bob, Billy",
likes : "meat#2Cbeear#2Cwine,potatoes
}
where #2C (hex for comma) is a comma within the string.
I have this code which works fine
var str = "a,b,c#2Cd";
var arr = str.split(',');
function escapeCommas(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].indexOf("#2C") !== -1) {
var s = arr[i].replace("#2C", ',');
arr[i] = s;
}
}
return arr;
}
console.log(escapeCommas(arr));
http://jsfiddle.net/5hogf5me/1/
I have a lot of functions that process the JSON data often as
var name = str.split(',')[i];
I am wondering how I could extend or re-write .split to automatically replace #2C with a comma.
Thanks for any advice.
Edit: I think this is better:
var j = {
names: "Bob, Billy",
likes : "meat#2Cpotatoes"
};
var result = j.likes.replace(/#2C/g, ',');
// j.likes.replace(/#2C/ig, ','); - if you want case insensitive
// and simply reverse parameters if you want
console.log(result);
This was my initial approach:
var j = {
names: "Bob, Billy",
likes : "meat,potatoes"
}
var result = j.likes.split(",").join("#2C")
console.log(result);
// meat#2Cpotatoes
Or if you have it the reverse:
var j = {
names: "Bob, Billy",
likes : "meat#2Cpotatoes"
}
var result = j.likes.split("#2C").join(",")
console.log(result);
// meat,potatoes
[Updated to reflect feedback] - try at http://jsfiddle.net
var str = 'a,b,c#2Cd,e#2Cf#2Cg';
alert(str.split(',').join('|')); // Original
String.prototype.native_split = String.prototype.split;
String.prototype.split = function (separator, limit) {
if ((separator===',')&&(!limit)) return this.replace(/,/g,'\0').replace(/#2C/gi,',').native_split('\0');
return this.native_split(separator, limit);
}
alert(str.split(',').join('|')); // Enhanced to un-escape "#2C" and "#2c"
String.prototype.split = String.prototype.native_split;
alert(str.split(',').join('|')); // Original restored
Couple minor tangential notes about your function "escapeCommas": this function is really doing a logical "un-escape" and so the function name might be reconsidered. Also, unless it is your intention to only replace the first occurence of "#2C" in each item then you should use the "g" (global) flag, otherwise an item "c#2Cd#2Cde" would come out "c,d#2Ce".

How to return a substring between two matching substrings in JavaScript?

I am brand new to programming and just getting started in an online program. The problem I am being presented with is:
Return a substring between two matching substrings.
The string I'm using is:
"Violets are blue, the sky is really blue"
I am trying to produce the substring between the two "blue"s.
That is:
", the sky is really "
This was one of my attempts which doesn't work. I was trying to slice it using indexOf() and lastIndexOf().
module.exports.substringBetweenMatches = function(text, searchString) {
return text.substring(function indexOf(searchString), function lastIndexOf(searchString);
};
module.exports.substringBetweenMatches("Violets are blue, the sky is really blue", "blue");
Any suggestions would be appreciated.
If the string will potentially have more than 2 "matches", you could split the string on the matches, then loop through and concat the strings back together:
var array = text.split(searchString); // split the given text, on the search term/phrase
if (array.length > 2) { // check to see if there were multiple sub-sections made
var string = "";
for (var i = 1; i < array.length; i++) { // start at 1, so we don't take whatever was before the first search term
string += array[i]; // add each piece of the array back into 1 string
}
return string;
}
return array[1];
This is pretty much the idea. I might have messed up the syntax for JavaScript in some places, but the logic is such:
function endsWith(a, s) {
var does_it_match = true;
var start_length = a.length()-s.length()-1;
for (int i=0; i<s.length(); i++) {
if (a[start_length+i]!=s.charAt(i)) {
does_it_match = false;
}
}
return does_it_match;
}
var buffer = new Array();
var return_string = "";
var read = false;
for (int i=0; i<string1.length(); i++) {
buffer.push(string1.charAt(1));
if (endsWith(buffer, "blue") && read==false) {
buffer = new Array();
read = true;
}
else if(endsWith(buffer, "blue") && read==true) {
break;
}
if (read==true) {
return_string = return_string.concat(string1.charAt(i));
}
}
return return_string;
I have stumbled upon this problem myself as a student on the Bloc.io bootcamp program. I stuck to the lessons string.substring() method and also string.indexOf() methods. Here is my go at this answer.
substringBetweenMatches = function(text, searchString) { //where text is your full text string and searchString is the portion you are trying to find.
var beginning = text.indexOf(searchString)+searchString.length; // this is the first searchString index location - the searchString length;
var ending = text.lastIndexOf(searchString); // this is the end index position in the string where searchString is also found.
return(text.substring(beginning,ending)); // the substring method here will cut out the text that doesn't belong based on our beginning and ending values.
};
If you are confused by my code, try:
console.log(beginning);
and
console.log(ending);
to see their values and how they would work with the substring() method.
Here is great reference to the substring() method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring
Here is a JS Fiddle test. I use alert() instead of return. The concept is similar. https://jsfiddle.net/felicedeNigris/7nuhujx6/
I hope that is clear enough with my long comments on the sides?
Hope this helps.

How would you convert this string to JSON?

I need to serialize this string into a multidimensional array using JSON. How would you do it?
{frmb[0][cssClass]=textarea&frmb[0][required]=true&frmb[0][values]=para&frmb[1][cssClass]=radio&frmb[1][required]=true&frmb[1][title]=rdo&frmb[1][values][2][value]=one&frmb[1][values][2][baseline]=true&frmb[1][values][3][value]=two&frmb[1][values][3][baseline]=false&frmb[2][cssClass]=input_text&frmb[2][required]=false&frmb[2][values]=text&frmb[3][cssClass]=checkbox&frmb[3][required]=true&frmb[3][title]=chk&frmb[3][values][2][value]=chk+1&frmb[3][values][2][baseline]=true&frmb[3][values][3][value]=chk+2&frmb[3][values][3][baseline]=false&frmb[4][cssClass]=select&frmb[4][required]=false&frmb[4][multiple]=true&frmb[4][title]=sel&frmb[4][values][2][value]=sel1&frmb[4][values][2][baseline]=true&frmb[4][values][3][value]=sel2&frmb[4][values][3][baseline]=false&form_id=undefined}
EDIT:
The multidimensionality is really screwing me up on converting this string into JSON. I tried stripping out the brackets to get a list of indices for the array, but rebuilding the array in a useful manner has been the biggest challenge.
So I have been doing:
private static List<string> StripBrackets(string input)
{
var ret = new List<string>();
var indx = 0;
do
{
input = input.TrimStart('[');
indx = input.IndexOf(']');
if(indx !=-1)
{
var newVal = input.Substring(0, indx);
ret.Add(newVal);
input = input.Remove(0, indx+1);
}
} while (indx != -1);
//-- this is where I bonked
string[,] results = new string[ret.Count, 23];
foreach (var r in ret)
{
}
return ret;
}
I've been grinding on this for a long time - hence the tired/limited question. It started to feel like I was doing this the "hard way" and wanted to see if anyone enlightened had any better ideas. TIA
Expanding on #ratchetfreak's comment:
var str = "{frmb...";
//sanitize string
str = str.substring(1, str.length-1); //remove braces
str = str.replace(/\[([a-z]+)\]/gi, '["$1"]'); //enclose non-numeric keys in double quotes
str = str.replace(/=([^&]+)/g, '="$1"'); //enclose values in double quotes
str = str.replace(/"(true|false)"/g, "$1"); //remove double quotes around boolean values
str = str.replace(/&/g, ";"); //replace all ampersands with semi-colons
//initialize "frmb"
var frmb = [];
for (var i = 0; i < 5 /* a guess as to how long "frmb" will be */; i++){
var values = [];
for (var j = 0; j < 5 /* a guess as to how long each "values" field will be */; j++){
values.push({});
}
frmb.push({values:values});
}
//evaluate as Javascript
eval(str);
//marshal to string
var result = JSON.stringify(frmb);
The part that makes this solution difficult to make general-purpose is that you need to initialize "frmb". For example, you can't call frmb[0].cssClass without first initializing frmb to an array and then adding an object to that array with push(). So, you need to estimate how large you think frmb will be, as well as how large each values sub-array will be.

How to split a URL string with parameters into an array using JavaScript

I'm trying to break up a string like this one:
fname=bill&mname=&lname=jones&addr1=This%20House&...
I want to end up with an array indexed like this
myarray[0][0] = fname
myarray[0][1] = bill
myarray[1][0] = mname
myarray[1][1] =
myarray[2][0] = lname
myarray[2][1] = jones
myarray[3][0] = addr
myarray[3][1] = This House
The url is quite a bit longer than the example. This is what I've tried:
var
fArray = [],
nv = [],
myarray = [];
fArray = fields.split('&');
// split it into fArray[i]['name']="value"
for (i=0; i < fArray.length; i++) {
nv = fArray[i].split('=');
myarray.push(nv[0],nv[1]);
nv.length = 0;
}
The final product is intended to be in 'myarray' and it is, except that I'm getting a one dimensional array instead of a 2 dimensional one.
The next process is intended to search for (for example) 'lname' and returning the index of it, so that if it returned '3' I can then access the actual last name with myarray[3][1].
Does this make sense or am I over complicating things?
Your line myarray.push(nv[0],nv[1]); pushes two elements to the array myarray, not a single cell with two elements as you expect (ref: array.push). What you want is myarray.push( [nv[0],nv[1]] ) (note the brackets), or myarray.push(nv.slice(0, 2)) (ref: array.slice).
To simplify your code, may I suggest using Array.map:
var q = "foo=bar&baz=quux&lorem=ipsum";
// PS. If you're parsing from a-tag nodes, they have a property
// node.search which contains the query string, but note that
// it has a leading ? so you want node.search.substr(1)
var vars = q.split("&").map(function (kv) {
return kv.split("=", 2);
});
For searching, I would suggest using array.filter:
var srchkey = "foo";
var matches = vars.filter(function (v) { return v[0] === srchkey; });
NB. array.filter will always return an array. If you always want just a single value, you could use array.some or a bespoke searching algorithm.
for (var i = 0; i < fArray.length; i++) {
nv = fArray[i].split('=');
myarray.push([nv[0],nv[1]]);
}
nv.length = 0; is not required, since you're setting nv in each iteration of the for loop.
Also, use var i in the for-loop, otherwise, you're using / assigning a global variable i, that's asking for interference.

Categories