Hi I have cookie which has value of serialized params.
i.e)criteria = "name=praveen&age=25&studying=false&working=true&something=value"
Now I have to update name = praveenkumar,age = 24,something = null in this cookie string. If value is null(something = null) then it should be removed from cookie. The following is the code I am using.
updateCookie({'name':'praveenkumar','age':24,'something':null})
var updateCookie = function(params) {
// split the cookie into array
var arr = $.cookie('criteria').split("&");
//loop through each element
for(var i = arr.length - 1; i >= 0; i--) {
// getting the key i.e) name,age
var key = arr[i].split("=")[0];
// if the key is in given param updating the value
if( key in params) {
// if vale null remove the element from array or update the value
if(params[key] !== null) {
arr[i] = key+"="+params[key];
}
else {
arr.splice(i, 1);
}
}
}
// join array by & to frame cookie string again
$.cookie('criteria' , arr.join("&"));
};
It is working. But I am concerned about performance if the size of cookie become more.
Way too complicated. Try this instead :
var criteria = "name=praveen&age=25&studying=false&working=true&something=value";
Turn the serialized string into a object
var obj = JSON.parse('{"' + decodeURI(criteria)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g,'":"')
+ '"}')
Based on this answer. Now you can manipulate the object
obj.studying = null;
Remove null values from the object
for (prop in obj) {
if (obj[prop] == null) delete obj[prop]
}
use jQuery's $.param to get the modified object as serialized string
criteria = $.param(obj);
== name=praveen&age=25&working=true&something=value
save the updated cookie value
$.cookie('criteria', criteria);
I do not understand the concern about performance, You should never expect a browser to allow more than 4096 bytes in a cookie. If performance and size is a concern you should use localStorage. It is much faster than cookies and has capacity measured in MB's (vary from browser to browser).
Related
Im using the following code,
jQuery.each(aDataSel, function(index, oData) {
oPushedObject = {};
aSelectedDataSet.push(fnCreateEnt(aProp, oData, oPushedObject));
});
This is aSelectedDataSet values
and this is the values of OData
What I need is that before I do the push is to fill the listTypeGroup & listTypeGroupDescription (with the red arrow ) with values that Are inside the oData -> ListTypeGroupAssigment -> result (listTypeGroup & listTypeGroupDescription) , The index is relevant since I want to add just the value of the index in each iteration (since this code is called inside outer loop and the index determine the current step of the loop) ,How it can be done nicely?
The result contain 100 entries (always) and the a selected data will have 100 entries at the end...
Update :)
Just to be clear In the pic I show the values which is hardcoded for this run but the values can be any values, we just need to find the match between the both objects values...
I mean to find a match between to_ListTypeGroupAssigment in both object (which in this case exist ) and if in oData there is result bigger then one entry start with the matching ...
UPDATE2 - when I try Dave code the following happen for each entry,
This happen in the Jquery.extend line...any idea how to overcome this?
The following hard-coded of Dave:-) work perfect but I need generic code which doesnt refer to specific field name
jQuery.each(aDataSet, function(index, oData) {
oPushedObject = {};
fnCreatePushedEntry(aProperties, oData, oPushedObject);
var result = oData.to_ListTypeGroupAssignment.results[index];
oPushedObject.to_ListTypeGroupAssignment = {
ListTypeGroup: result.ListTypeGroup,
ListTypeGroupDescription: result.ListTypeGroupDescription
};
aSelectedDataSet.push(oPushedObject);
});
Im stuck :(any idea how to proceed here ?what can be wrong with the extend ?
should I use something else ? Im new to jQuery...:)
I think that this happen(in Dave answer) because the oData[key] is contain the results and not the specified key (the keyValue = to_ListTypeGroupAssignment ) which is correct but we need the value inside the object result per index...
var needValuesForMatch = {
ListTypeGroup: 'undefined',
ListTypeGroupDescription: 'undefined',
}
//Just to show that oPushedObject can contain additional values just for simulation
var temp = {
test: 1
};
//------------------This object to_ListTypeGroupAssigment should be filled (in generic way :) ------
var oPushedObject = {
temp: temp,
to_ListTypeGroupAssignment: needValuesForMatch
};
oPushedObject is one instance in aSelectedDataSet
and after the matching I need to do the follwing:
aSelectedDataSet.push(oPushedObject);
Is this what you're after:
OPTION ONE - DEEP CLONE FROM oData TO aSelectedDataSet
aSelectedDataSet.forEach(function(currentObject,index){
for (var childObject in currentObject) {
if (! currentObject.hasOwnProperty(childObject))
continue;
var objectToClone = oData[childObject]['results'][index];
if(objectToClone)
$.extend(true,currentObject[childObject],objectToClone);
}
});
Here is your data in a fiddle with the function applied: https://jsfiddle.net/hyz0s5fe/
OPTION TWO - DEEP CLONE FROM oData ONLY WHERE PROPERTY EXISTS IN aSelectedDataSet
aSelectedDataSet.forEach(function(currentObject,index){
for (var childObject in currentObject) {
if (! currentObject.hasOwnProperty(childObject))
continue;
if(typeof currentObject[childObject] !== 'object')
continue;
for(var grandChildObject in currentObject[childObject]) {
var objectToClone = oData[childObject]['results'][index][grandChildObject];
if(typeof objectToClone === 'object') {
$.extend(true,currentObject[childObject][grandChildObject],objectToClone);
} else {
currentObject[childObject][grandChildObject] = objectToClone;
}
}
}
Fiddle for option 2: https://jsfiddle.net/4rh6tt25/
If I am understanding you correctly this should just be a small change:
jQuery.each(aDataSel, function(index, oData) {
oPushedObject = {};
fnCreateEnt(aProp, oData, oPushObj);
//get all the properties of oData and clone into matching properties of oPushObj
Object.getOwnPropertyNames(oData).forEach(function(key) {
if (oPushObj.hasOwnProperty(key)) {
//oPushObj has a matching property, start creating destination object
oPushObj[key] = {};
var source = oData[key];
var destination = oPushObj[key];
//can safely assume we are copying an object. iterate through source properties
Object.getOwnPropertyNames(source).forEach(function(sourceKey) {
var sourceItem = source[sourceKey];
//handle property differently for arrays
if (Array.isArray(sourceItem)) {
//just copy the array item from the appropriate index
destination[sourceKey] = sourceItem.slice(index, index + 1);
} else {
//use jQuery to make a full clone of sourceItem
destination[sourceKey] = $.extend(true, {}, sourceItem);
}
});
}
});
aSelectedDataSet.push(oPushedObject);
});
It is unclear what exactly your fnCreateEnt() function returns though. I am assuming it is the populated oPushObj but it's not entirely clear from your question.
I'm working on a script, where you pass it a url like /search?filter1=question-1&filter2=question2, and when either question-1 or question-2 is changed, it will take the url, and replace the question-x with the question value.
One thing I want to build in, is if the value is empty, I want it to remove the query string part. So for example, if question-1 has a value of something, but 2 doesn't have a value yet, the url will be /search?filter1=something.
What I thought would work would be something like this
$url.match('/([^?&]*)' + name.toSearch + '/g') // toSearch is a value like question-1
But that returns null. Can anybody help me figure out what I need to change to get the output I'm after?
Given the url /search?filter=question-1, I need to see if the element with the name question[1] has a value, if it does, replace question-1 with the value, and if it doesn't have one, remove the total filter=question-1 string.
Knowing your requirements better from the comments, I completely rewrote my answer using parts of my original answer and some of your code:
// Given url
var url = '/search?filter1=question-1&filter2=question-2';
// Specify the filters so no extra query string noise enters the final url
var filters = ["filter1", "filter2", "filter3"];
// Split the query string parts
var urlParts = url.split(/([&?])/);
var reassembled = [];
// Break the url parts into key:value pairs
var qs = (function(a) {
if (a === "") return {};
var b = {};
for (var i = 0; i < a.length; ++i)
{
var p=a[i].split('=', 2);
if (p.length == 1)
b[p[0]] = "";
else
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
}
return b;
})(urlParts);
// This include a param:value in the reassembled array
function includeQSParam(param, value) {
if(qs[param]) {
reassembled.push(param + "=" + value);
}
}
// Run through the filters
for(var ind in filters) {
var filter = filters[ind];
// Check that the filter exists and the supplied value is of type question-
if(qs[filter] && qs[filter].indexOf("question-") >= 0) {
// Turns question-number into question[number] so it's a valid selector.
var inputSelector = "question["+(qs[filter]).replace(/\D/g, "")+"]";
// Get the input, and the value (author-supplied code)
var $input = $('[name="' + inputSelector + '"]');
// TODO: confirm this is how you get the value
var value = $input.closest('.question').val();
if($input.length > 0 && (value !== '' && value !== undefined)) {
// Replace the parameter's original value with the question value
includeQSParam(filter, value);
} else {
// Nothing to do. This filter will be removed automatically
}
}
}
// Reassemble the URL
var fixedUrl = urlParts[0] + (reassembled.length > 0 ? "?"+reassembled.join("&") : "");
Again, this was reworked from my original answer so there will be some bloat, but I didn't want to abandon the question on you.
Whilst Drakes answer is a good one, it didn't quite fit into my needs. I've ended up with this, which works well so far, but I'm still testing it.
var $url = '/search?filter1=question-1&filter2=question-2';
// Break the url into parts.
var $split = $url.split(/([&?])/);
$.each($split, function(indexToRemove, part){
// If the part is a question.
if(typeof part == 'string' && part.indexOf('question-') > -1){
var $number = part.split('=');
// Turns question-number into question[number] so it's a valid selector.
$inputSelector = String($number[1]).replace('-', '[') + ']';
// Get the input, and the value.
var $input = $('[name="' + $inputSelector + '"]');
var $value = getValue($input.closest('.question'));
// If there is an element, and there is a value.
if($input.length > 0 && ($value != '' && $value != undefined)){
$split[indexToRemove] = part.replace($number[1], $value);
} else {
$split.splice(indexToRemove, 1);
}
}
});
$url = $split.join('');
// If for example question-1 has a value of 'Test', and question-2 has no value, $url will now be '/search?filter1=Test'.
I'm trying to save numbers given by Math.random. I save them in array which gets saved into localStorage. I then want to append each new array of numbers when Math.random is used. It's easier if you view the code I tried wrting.
var nums = [];
for (var i = 0; i < 5; i++) {
var num = Math.floor(Math.random() * 50);
nums.push(" " + num);
}
console.log(nums);
function appendToStorage(name, data) {
var old = localStorage.getItem(name);
if (old === null) old = "";
localStorage.setItem(name, old + JSON.stringify(data));
}
if (localStorage.num) {
appendToStorage('num', nums);
} else {
localStorage.setItem("num", JSON.stringify(nums));
}
var nums2 = localStorage.getItem("num");
console.log(nums2);
document.getElementById("test").innerHTML = JSON.parse(nums2);
This doesn't work, though. Console says Uncaught SyntaxError: Unexpected token [
This error will work if you remove the JSON.parse on getElementById. I want it to be parsed, though, so the numbers are more easily viewed. How can I do this?
If you simply append a valid JSON string to another valid JSON string you don't get a valid JSON string. For example:
var myJSON = '{"thing": "data"}'; // valid
myJSON = myJSON + myJSON; // myJSON is now '{"thing": "data"}{"thing": "data"}', not valid
To do this reliably you'll need to parse your retrieved JSON, update the result, then stringify it again before storing it in localStorage.
function appendToStorage(name, data) {
var old = localStorage.getItem(name);
if (old === null) old = "[]";
var newData = JSON.parse(old);
newData.push(data);
localStorage.setItem(name, JSON.stringify(newData));
}
Note that this will return an array when you parse it, and that will cause you a problem when you try to set innerHTML. You'll need to unpack the array to some sort of text format first (thanks to jibsales for that).
Element.innerHTML takes a string as valid input, not an Array. May I suggest using JSON.parse(nums).join("");
Using this method would also allow you to not add the leading white space in your for loop and instead add the white space as the first parameter to the Array.join method. If you want each number on a new line, pass "\n".
I have the following array that contains user data. There are only about 20 elements in thsi data. I get this from my server and it is stored locally:
var userdata1 =
[
{"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4","name":"xxx"},
{"id":"e87c05bc-8305-45d0-ba07-3dd24438ba8b","name":"yyy"}
]
I have been using the following function to get the user name from my userProfiles array.
$scope.getUser = function (userId) {
if (userId && $scope.option.userProfiles)
for (var i = 0; i < $scope.option.userProfiles.length; i++)
if ($scope.option.userProfiles[i].id === userId)
return $scope.option.userProfiles[i].name;
return '';
}
I was looking for a more efficient way to get the name so I asked this question:
How can I check an array for the first occurence where a field matches using _lodash?
Now I am wondering. Is there another way that I could store my data to make it easier to access? One person suggested this
in the comments:
var usersdata2 = {someuserid: {id: "someusersid", name: 'Some Name'},
anotheruserid: {id: "anotheruserid", name: 'Another Name'}};
If I was to do this then would it be more efficient, how could I change my data from the first form userdata1 into userdata2
and how could I access it?
You can transform your array as follows:
var userMap = userdata1.reduce(function(rv, v) {
rv[v.id] = v;
return rv;
}, {});
That will give you an object that maps the "id" values onto the original object. You would then access the values like this:
var someUser = userMap[ someUserId ];
This set up will be much more efficient than your array, because finding an entry takes an amount of time proportional to the size of the "id" strings themselves (plus a little). In your version, you have to search through (on average) half the list for each lookup. For a small set of records, the difference would be unimportant, but if you've got hundreds or thousands of them the difference will be huge.
The .reduce() function is not available in older browsers, but there's a fill-in patch available on the MDN documentation site:
// copied from MDN
if ('function' !== typeof Array.prototype.reduce) {
Array.prototype.reduce = function(callback, opt_initialValue){
'use strict';
if (null === this || 'undefined' === typeof this) {
// At the moment all modern browsers, that support strict mode, have
// native implementation of Array.prototype.reduce. For instance, IE8
// does not support strict mode, so this check is actually useless.
throw new TypeError(
'Array.prototype.reduce called on null or undefined');
}
if ('function' !== typeof callback) {
throw new TypeError(callback + ' is not a function');
}
var index, value,
length = this.length >>> 0,
isValueSet = false;
if (1 < arguments.length) {
value = opt_initialValue;
isValueSet = true;
}
for (index = 0; length > index; ++index) {
if (this.hasOwnProperty(index)) {
if (isValueSet) {
value = callback(value, this[index], index, this);
}
else {
value = this[index];
isValueSet = true;
}
}
}
if (!isValueSet) {
throw new TypeError('Reduce of empty array with no initial value');
}
return value;
};
}
Try something like this:
var usernames = {};
userdata1.forEach(function(u) {usernames[u.id] = u.name;});
alert(usernames[userId]);
(You'll either need a shim or a manual for loop to support older browsers - the above is intended to just give you an idea on how you can simplify your access)
To make the access by ID more efficient copy the data into an object:
var userdata1 =
[
{"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4","name":"xxx"},
{"id":"e87c05bc-8305-45d0-ba07-3dd24438ba8b","name":"yyy"}
];
var userIdMap = {};
for (var i = 0; i < userdata1.length; i++) {
var item = userdata1[i];
userIdMap[item.id] = item;
}
which means the function is now:
$scope.getUser = function (userId) {
if (userId && $scope.option.userProfiles) {
var user = userIdMap[userId];
if(user)
return user.name;
}
return '';
}
Here is a function that puts your array items into a lookup object:
function arrayToLookup(array, idProperty) {
var result = {};
for (var i = array.length - 1; i >= 0; i--) {
result[array[i][idProperty]] = array[i];
}
return result;
}
Usage would be like this, for your example:
var userdata1 =
[
{"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4","name":"xxx"},
{"id":"e87c05bc-8305-45d0-ba07-3dd24438ba8b","name":"yyy"}
]
// create a lookup object of your array.
// second parameter is the name of the property to use as the keys
var userDataLookup = arrayToLookup(userdata1, 'id');
// this is how you get a specific user out of the lookup
var user = userDataLookup["527ddbd5-14d3-4fb9-a7ae-374e66f635d4"];
I'm currently using javascript eval() to check and create a multidimensional object that I have no idea of the depth.
Basically, I want to know if there's any way to create this multi-depth object. The object can be as deep as result['one']['two']['three']['four']['five']['six']['seven']. I know there are cases where using eval() is perfectly fine, but I'm also worried about performance. I thought about referencing each depth to a new variable, but I don't know how to do pointers in Javascript
create = function(fields, create_array){
var field;
for (j = 0; j < len; j++){
field = fields.slice(0, j).join('');
if (field){
// is there any way to do this without eval?
eval('if (typeof result' + field + ' == "undefined" || !result' + field + ') result' + field + ' = ' + (create_array?'[]':'{}') + ';');
}
}
}
How about
var deep = { one: { two: { three: { four: { five: { six: { seven: 'peek-a-boo!' }}}}}}};
I don't see what "eval()" has to do with this at all; there's no reason to "initialize" such an object. Just create them.
If you wanted to write a function with an API like you've got (for reasons I don't understand), you could do this:
function create(fields, create_array) {
var rv = create_array ? [] : {}, o = rv;
for (var i = 0; i < fields.length; ++i) {
o = o[fields[i]] = create_array ? [] : {};
}
return rv;
}
There doesn't seem to be any point to the "create_array" flag, since you're presumably always using strings for keys.
Never mind, found my way in. I used a recursive function to ensure that the object was created properly.
create = function(create_array, res, path){
var field = fields.shift();
if (field){
if (typeof res[field] == "undefined" || !res[field]) res[field] = (create_array?[]:{});
path.push('["' + field + '"]');
create(create_array, res[field], path);
}
}
var result = {}, strpath = [], fields[];
create(true, result, strpath);
eval('result' + strpath.join('') + ' = value;');
being variable "field" a variable outside the function, that contained the levels of the object. doing result["field"]["name"]["first"] = value without the ["field"] or ["name"] field existing or defined as an object, would throw an error and stop execution, that's why I'm pre-creating the object variable, either as an array or object.
I couldn't find another option for the second eval() though. There's no way to provide a way to access multiple properties on an object without knowing the depth.