Updating multi-level object value from dataset value - javascript

difficult one to explain.
var options = {
container: node,
pin: {
size: [50, 50],
anchor: 0,
animation: 0
}
}
Let's use the above Object as an example. I want to loop through the dataset from an HTMLElement and update the above values using the dataset values. This removed the need to manually check if the dataset value exists and then replace the value.
What I have got so far:
(function() {
for( const data in node.dataset ) {
// First remove the 'map' as this isn't required, then split any values with multiple capitals,
// as these corrospond to multilevel object values.
var key = (data.replace("map", "")).split(/(?=[A-Z])/), value = node.dataset[data];
const findOption = function() {
};
// Check that there is a value
if (value !== null) {
const opt = null;
// Find the corresponding default option
}
}
}.call(_));
Here is the HTML with the dataset attributes, this should help everything make more sense:
<div data-map data-map-offset='[10, 10]' data-map-pin-size='[20, 20]'></div>
As you can see above the attribute data-map-pin-size needs to replace the value within the object, but i'm not sure how to reference that object as usually I would either do options.pin.size or options['pin']['size']. But due to this loop not knowing how deep it needs to go I can't always rely on this, I need some kind of callback function right? Which is why I started findOption() however i'm not too sure where to go from there!
Edit:
This is what I have got so far now, however this isn't updating the options object, it's just setting the value of opt.
(function() {
for( const data in node.dataset ) {
// First remove the 'map' as this isn't required, then split any values with multiple capitals,
// as these corrospond to multilevel object values.
var key = (data.replace("map", "")).split(/(?=[A-Z])/), value = node.dataset[data];
// Pin Size
const findOption = function(val) {
return options[val.toLowerCase()];
};
// Check that there is a value
if (value !== null) {
var opt = null;
// Find the corresponding default option
for (var x = 0; key.length > x; x++) {
opt = findOption(key[x]);
}
opt = value;
}
}
console.log(options);
}.call(_));

If you convert your options to this format:
var options = {
container: node,
pinSize: [50, 50],
pinAnchor: 0,
pinAnimation: 0
}
your implementation would be able to be simplified to this:
for (const key in node.dataset) {
const opt = key.replace(/^map(.)/, (match, c) => c.toLowerCase())
options[opt] = JSON.parse(node.dataset[key])
}
assuming you intend to use JSON-compliant values in your HTML data- attributes.

Here I added a recursive function to set the value by finding the specific key on the options object. You can see that for any length of data set attribute the values are getting set properly.
This works with any kind of options object format dynamically.
I added an extra sample as well to demonstrate it.
(function() {
//A function to set the value on a nested object by
//recursively finding the key
function setValue(object, key, value) {
var value;
Object.keys(object).some(function(k) {
if (k === key) {
object[k] = value;
}
if (object[k] && typeof object[k] === 'object') {
setValue(object[k], key, value);
}
});
}
node = document.getElementById("elem");
var options = {
container: node,
pin: {
size: [50, 50],
anchor: 0,
animation: 0,
value: {
xy: [1, 1]
}
}
}
for (const data in node.dataset) {
// First remove the 'map' as this isn't required, then split any values with multiple capitals,
// as these corrospond to multilevel object values.
var keys = (data.replace("map", "")).split(/(?=[A-Z])/),
value = node.dataset[data];
var findOption = function() {
keys.forEach(function(key, index) {
if (index == keys.length - 1) {
setValue(options, key.toLowerCase(), value);
}
})
}();
// Check that there is a value
if (value !== null) {
const opt = null;
// Find the corresponding default option
}
}
console.log(options);
}.call());
<div data-map data-map-offset='[10, 10]' data-map-pin-size='[20, 20]' data-map-pin-value-xy='[0, 5]' id="elem"></div>

Related

Is there a way to replace a value in an array result of a custom search on Suitescripts?

I am trying to replace a value in a search.create function with a filter. the filter returns one or more objects, and i store them in an array. On accessing the array, i want to replace a certain value with 0, if a certain field 'lastinv' is empty. here is my code below;
// a is lastinv
if (a == null || a == '') {
todate = format.format({
value: todate,
type: format.Type.DATE
});
// creates a custom search on a record with specific filters
var reading = search.create({
type: 'customrecord_ew_meterreading_form',
columns: ['name', 'id', 'custrecord_ew_mr_metername', 'custrecordmeternumber', 'custrecord_ew_mr_site', 'custrecord1', 'custrecord_ew_meterreading_value'],
filters: [{
name: 'custrecord_ew_mr_site',
operator: search.Operator.IS,
values: site
}, {
name: 'custrecord1',
operator: search.Operator.ON,
values: todate
}] // end of filter*/
});
// gets a custom search for the readings of a site on the
// firstdate/lastdate of the month and stores in a array called ar_readings
reading.run().each(function(result) {
ar_reading.push(result);
return true;
});
return ar_reading;
}
And this next part is me trying to replace the value on a certain index;
for (var k = 0; k < firstReading.length; k++) {
ar_mtrdata[k] = new Array();
var l_meternumber = firstReading[k].getValue('custrecordmeternumber');
var mtrdtls = getMeterDetails(l_meternumber);
ar_mtrdata[k][0] = mtrdtls.getValue('itemid'); // Meternumber
if (lastinv == '' || lastinv == undefined || lastinv == null) {
ar_mtrdata[k][2] = 0;
} else {
ar_mtrdata[k][2] = firstReading[k].getValue('custrecord_ew_meterreading_value'); // previous reading
}
/* ... */
}
Why don't you extract the values within the first code block inside of the run().each loop?
The you could push a organized object into the array that already has the values the way you want them.

How to write case condition expression in DustJS?

I have a array type in Javascript:
var type=[0,1,2,3,4,5];
The corresponding key value pair collection is actually like this;
[{0:'Pointer'},{1:'Line'},{2:'Oval'},{3:'Rectangle'},{4:'Ellipse'},{5,'Star'}]
My .dust file displays the {type} mark, how should I display their corresponding values?
You may use a custom filter. The approximate implementation would be:
var mapping = {
'0':'Pointer',
'1':'Line',
'2':'Oval',
'3':'Rectangle',
'4':'Ellipse',
'5':'Star'
};
dust.filters['typeFilter'] = function(value) {
return mapping[value] || '';
}
And in you template: {type|typeFilter}
It's a bit simpler to store the mapping in an object than array. For you array version it would be like:
var mapping = [{0:'Pointer'},{1:'Line'},{2:'Oval'},{3:'Rectangle'},{4:'Ellipse'},{5:'Star'}];
dust.filters['typeFilter'] = function(value) {
var typeId = parseInt(value);
for(var len = mapping.length - 1; len >=0; len--) {
if (mapping[len].hasOwnProperty(typeId)) {
return mapping[len][typeId];
}
}
return '';
}
A bit more information here

Getting index from 2D array quickly without iteration jquery

I have this 2D array as follows:
var data = [[1349245800000, 11407.273], [1349247600000, 12651.324],
[1349249400000, 11995.017], [1349251200000, 11567.533],
[1349253000000, 11126.858], [1349254800000, 9856.455],
[1349256600000, 8901.779], [1349258400000, 8270.123],
[1349260200000, 8081.841], [1349262000000, 7976.148],
[1349263800000, 7279.652], [1349265600000, 6983.956],
[1349267400000, 7823.309], [1349269200000, 6256.398],
[1349271000000, 5487.86], [1349272800000, 5094.47],
[1349274600000, 4872.403], [1349276400000, 4168.556],
[1349278200000, 4501.939], [1349280000000, 4150.769],
[1349281800000, 4061.599], [1349283600000, 3773.741],
[1349285400000, 3876.534], [1349287200000, 3221.753],
[1349289000000, 3330.14], [1349290800000, 3147.335],
[1349292600000, 2767.582], [1349294400000, 2638.549],
[1349296200000, 2477.312], [1349298000000, 2270.975],
[1349299800000, 2207.568], [1349301600000, 1972.667],
[1349303400000, 1788.853], [1349305200000, 1723.891],
[1349307000000, 1629.002], [1349308800000, 1660.084],
[1349310600000, 1710.227], [1349312400000, 1708.039],
[1349314200000, 1683.354], [1349316000000, 2236.317],
[1349317800000, 2228.405], [1349319600000, 2756.069],
[1349321400000, 4289.437], [1349323200000, 4548.436],
[1349325000000, 5225.245], [1349326800000, 6261.156],
[1349328600000, 8103.636], [1349330400000, 10713.788]]
How do I get the index of value 1349247600000 in the array? I have tried $.inArray(1349247600000, data) but as expected this fails. Is there any other way or do I have to iterate over each? I am reluctant to add another loop to my process
This is a typical performance versus memory issue. The only way (that I know of) to avoid looping through the array, would be to maintain a second data structure mapping the timestamps to the index of the array (or whatever data might needed).
So you would have
var data = [
[1349245800000, 11407.273],
[1349247600000, 12651.324],
// ...
[1349330400000, 10713.788]
];
// the timestamps pointing at their respective indices
var map = {
'1349245800000': 0, // 0
'1349247600000': 1, // 1
// ...
'1349330400000': 42, // n - 1 (the length of the data array minus one)
}
This way, you use more memory, but have a constant lookup time when needing the index of the item in the array that a given timestamp belongs to.
To get the index of a given timestamp do:
map['1349247600000']; // resulting in 1 (e.g.)
If the data structure is dynamically changed, you would of course need to maintain the map data structure, but depending on the context in which you need the lookup, the constant time lookup can potentially be a real time saver compared to a linear time lookup.
I think you need a different data structure.
Try using a standard javascript object ({ key: value } - sometimes called a map or dictionary) to express your data. Looking up keys in an object is highly optimized (using something called hash tables).
If the index in your array has any meaning, store it as a property (typically named _id).
Ideally you should be using an object for this:
var data = {
'1349247600000': 12651.324
}
which you can access like:
data['1349247600000'];
However, this might be a nice solution (IE9 and above) in the meantime:
var search = 1349247600000;
function findIndex(data, search) {
var filter = data.filter(function (el, i) {
el.unshift(i);
return el[1] === search;
});
return filter[0][0];
}
console.log(findIndex(data, search));
fiddle : http://jsfiddle.net/CLa56/
var searchElement = 1349251200000;
var strdata = data.toString();
var newdata = eval("[" + strdata + "]");
var indexsearch = newdata.indexOf(searchElement);
var index = indexsearch/2; // 2 because array.length = 2
var params = {id: 1349251200000, index: -1};
data.some(function (e, i) {
if (e[0] === this.id) {
this.index = i;
return true;
}
}, params);
console.log(params.index);
jsfiddle
MDN|some Array Method
Note that this solution stops iterating after found, not necessarily over the entire array, so could be much faster for large arrays.
What about a custom cross browser solution ?
function findIndexBy(a, fn) {
var i = 0, l = a.length;
for (; i < l; i++) {
if (fn(a[i], i)) {
return i;
}
}
return -1;
}
Usage :
var list = [[1],[2],[3]], idx;
// idx === 1
idx = findIndexBy(list, function (item, i) {
return item[0] === 2;
});
// idx === -1
idx = findIndexBy(list, function (item, i) {
return item[0] === 4;
});

How to serialize a form into an object (with tree structure)?

I have a form
<form>
<input type="text" name="Name" />
<input type="checkbox" name="Feature.Translate" />
<input type="checkbox" name="Feature.Share" />
<input type="submit" value="Convert into an object" />
</form>
I want to convert it in an object
{
Name: "John Connor's Terminator",
Feature:
{
Translate: true // if checked
// Share wasn't checked
}
}
How can I map the form to an object that has this tree structure?
Add this method to help you build the tree
// add keys to an object as a tree
// ["a", "b", "c"] will generate
// a { b: { c: def } }
// def is the value of the leaf node
var AddToTree = function(obj, keys, def)
{
for (var i = 0, length = keys.length; i < length; ++i)
obj = obj[keys[i]] = i == length - 1 ? def : obj[keys[i]] || {};
};
Create a function for a jQuery selector that will convert the form in an object
$.fn.serializeObject = function()
{
var o = {}; // final object
var a = this.serializeArray(); // retrieves an array of all form values as
// objects { name: "", value: "" }
$.each(a, function() {
var ns = this.name.split("."); // split name to get namespace
AddToTree(o, ns, this.value); // creates a tree structure
// with values in the namespace
});
return o;
};
With these two functions define you can set an event on the submit button:
$(":submit").click(function(e){
// contains the object from the form
// respecting element namespaces
var obj = $("form").serializeObject();
});
Something like the following should work:
function serializeData() {
//this is where we'll store our serialized data
var serializedData = {};
//iterate over input, select, and textarea elements
jQuery("input, select, textarea").each(function(index) {
var $element = jQuery(this);
var name = $element.attr("name");
//we only want to serialize the element if it has a 'name' attribute
if(typeof name != "undefined") {
//split on the . to get an array
var parts = name.split(/\./);
//start building the serialized data
var currentPart = serializedData;
for(var i = 0; i < parts.length; i++) {
//if this particular element doesn't already exist in our hash, create it
//and initialize it to an empty hash
if(typeof serializedData[parts[i]] == "undefined") {
currentPart[parts[i]] = {};
}
//if we're currently looking at the very last element in the array then
//it means that we need to set its value to the value of the corresponding
//input element. Otherwise, it means that there are still keys within the
//array and so we set `currentPart` to the new hash that we just created
if(i == parts.length - 1) {
//if the element is a checkbox or a radio, we need to see if it's checked
//instead of looking at its value
if($element.attr("type").toLowerCase() == "checkbox" || $element.attr("type").toLowerCase() == "radio") {
currentPart[parts[i]] = $element.is(":checked");
}
else {
currentPart[parts[i]] = $element.val();
}
}
else {
currentPart = currentPart[parts[i]];
}
}
}
});
console.log(serializedData);
}
Check out the fiddle.
All you need to do now is to bind serializeData to the submit event on the form.

How can I restore the order of an (incomplete) select list to its original order?

I have two Select lists, between which you can move selected options. You can also move options up and down in the right list.
When I move options back over to the left list, I would like them to retain their original position in the list order, even if the list is missing some original options. This is solely for the purpose of making the list more convenient for the user.
I am currently defining an array with the original Select list onload.
What would be the best way to implement this?
You can store the original order in an array, and when inserting back, determine what's the latest element in the array that precedes the one to be inserted AND matches what's currently in the select list. Then insert after that.
A better solution is to just store the old array whole and re-populate on every insertion with desired elements as follows (warning: code not tested)
function init(selectId) {
var s = document.getElementById(selectId);
select_defaults[selectId] = [];
select_on[selectId] = [];
for (var i = 0; i < s.options.length; i++) {
select_defaults[selectId][i] = s.options[i];
select_on[selectId][i] = 1;
var value = list.options[i].value;
select_map_values[selectId][value] = i if you wish to add/remove by value.
var id = list.options[i].id; // if ID is defined for all options
select_map_ids[selectId][id] = i if you wish to add/remove by id.
}
}
function switch(selectId, num, id, value, to_add) { // You can pass number, value or id
if (num == null) {
if (id != null) {
num = select_map_ids[selectId][id]; // check if empty?
} else {
num = select_map_values[selectId][value]; // check if empty?
}
}
var old = select_on[selectId][num];
var newOption = (to_add) : 1 : 0;
if (old != newOption) {
select_on[selectId][num] = newOption;
redraw(selectId);
}
}
function add(selectId, num, id, value) {
switch(selectId, num, id, value, 1);
}
function remove(selectId, num, id, value) {
switch(selectId, num, id, value, 0);
}
function redraw(selectId) {
var s = document.getElementById(selectId);
s.options.length = 0; // empty out
for (var i = 0; i < select_on[selectId].length; i++) {
// can use global "initial_length" stored in init() instead of select_on[selectId].length
if (select_on[selectId][i] == 1) {
s.options.push(select_defaults[selectId][i]);
}
}
}
I would assign ascending values to the items so that you can insert an item back in the right place. The assigned value stays with the item no matter which list it's in.

Categories

Resources