How to get corresponding value of key in JavaScript - javascript

I have this code:
var getValuesArray = [];
var setValuesArray = [];
function SetValueJson(key, value, scormVersion, methodCalled)
{
if (key1 != null) {
var obj = {
key: key1,
value: value1
}
setValuesArray.push(obj);
alert("pushing the key as: " + setValuesArray[key] + " and value as: " + setValuesArray[key].value); //not shure how te reference it?
return value;
}
and:
function GetValueJson(key, scormVersion, methodCalled) {
//I will get to this later, want to get the array right first
}
How do I reference the array?alert("pushing the key as: " + setValuesArray[key] + " and value as: " + setValuesArray[key].value); is not correct..
thanks

I suggest you to use Object instead of Array.
Modified code
var setValuesArray = {};
function SetValueJson(key, value, scormVersion, methodCalled)
{
setValuesArray[key]= value;
alert("pushing the key as: " + key + " and value as: " + value);
return value;
}
function GetValueJson(key, scormVersion, methodCalled) {
return setValuesArray[key]; // will return 'undefined' if key is not present.
}

Well, this basically depends on what you want to do with this data structure.
If keeping these keys and values sequentially and their order is important than you would need to store them in an array.
If you only need to look up the values based on keys, then a simple object would do just fine.
Lets say you need to maintain the order of the key-value data as it comes in, and you're using an array for that, then you have two options:
Keep the actual key-value store inside an object. For the order, maintain a separate array only keeping the keys. Here's what it should look like somewhat
var keyValueStore = {},
keyOrderStore = [];
function store(key, value) {
keyValueStore[key] = value;
keyOrderStore.push(key);
}
function getValueByIndex(index) {
// do some bounds checking ofcourse :)
return keyValueStore[keyOrderStore[index]];
}
function getValue(key) {
return keyValueStore[key];
}
This is in my opinion a better option. However, if you don't want to maintain and sync two separate data structures, you can use this:
var storage = [];
function store(key, value) {
storage.push({
key: key,
value: value
});
}
function getAtIndex(index) {
return storage[index].value;
}
function getValue(key) {
for (var i = storage.length-1; i >= 0; i--) {
var obj = storage[i];
if (obj.key === key) return obj.value;
}
}
Hope this helps.

Related

Can we specify a generic getter on an object in javascript?

I want to do:
properties.email.value without triggering an error like: Can't read 'value' of 'undefined'
However, I don't want to do:
properties.email && properties.email.value and I don't want to use an helper, something like: get(properties, 'email.value').
I really want to keep the syntax properties.email.value
I can solve this by doing:
Object.defineProperty(properties, 'email', {
get: () => properties.email && properties.email.value,
enumerable: true,
configurable: true
});
Now the getter is in charge of doing my safety check. Perfect.
But I also want to be able to do properties.name.value safely.
But as the properties object comes from the API (json), I don't know the full list of properties possible.
So, is there a way to use this "magical" get syntax for any prop access like: properties[ANYTHING].value ?
OK, I've got something like this.
But you must create properties that way.
Hope this help :)
var properties = {
phone : {
value: "123456789"
}
}
var handler = {
get: function(target, name) {
return target.hasOwnProperty(name) ? target[name] : {};
}
};
var new_properties = new Proxy(properties, handler);
console.log("phone.value = " + new_properties.phone.value);
console.log("email.value = " + new_properties.email.value);
new_properties.email = {
value: 1
};
console.log("email.value after assign = " + new_properties.email.value);
The document reference here.
Edited
Even if the original properties object is unknown, this kind of usage works as well.
You could use a Proxy and get known properties and a custom result for unknow properties.
For changing properties, you could take the same approach and set the value.
var properties = { email: { value: 'foo#example.com' } },
proxy = new Proxy(
properties,
{
get: function(target, prop, receiver) {
if (prop in target) {
return target[prop] && target[prop].value
} else {
return;
}
},
set: function(target, prop, value) {
if (prop in target) {
target[prop].value = value;
} else {
target[prop] = { value };
}
}
}
);
console.log(proxy.email);
console.log(proxy.bar);
proxy.email = '41';
console.log(proxy.email);
I can't believe I'm doing this...
var wordlength = 7;
var alphabet="abcdefghijklmnopqrstuvwxyz";
alphabet += alphabet.toUpperCase() + "0123456789_";
var alen = alphabet.length;
var buildWord = function(number){
if(number===0){
return '';
}
return alphabet[number%alen]+buildWord(Math.floor(number/alen));
};
var total = Math.pow(alen, wordlength);
for(var i = 1; i<total; i++){
var w = buildWord(i);
if(isNaN(w[0]) && Object.prototype[w]===undefined){
Object.prototype[w]={};
}
}

Mixing Index Array with "Associative Array"

Since I need to access my items sometime by index and sometime by code. Is it a good idea to mix integer index with string index?
Note that the code, index, amount of items never changes after the data is loaded.
I'm thinking of doing something like this, where the same object is pushed and set as a hashtable.
function DataInformation(code, dataValue) {
this.code = code;
this.dataValue = dataValue;
}
var dataList = [];
function fillDataList() {
addNewData(new DataInformation("C1", 111));
addNewData(new DataInformation("C2", 222));
addNewData(new DataInformation("C3", 333));
}
function addNewData(newData) {
dataList.push(newData);
dataList[newData.code] = newData;
}
Then I would be able to access the object with either:
dataList[0].dataValue
dataList["C1"].dataValue
Before I used to loop to find the item.
function findItemByCode(code) {
for (var i = 0; i < dataList.length; i++) {
if (dataList[i].code == code) {
return dataList[i];
}
}
return null;
}
findItemByCode("C1").dataValue
Do you ever need to iterate dataList in strict order? Or is it just a bag of items for which you want random access by a certain key?
If ordered iteration is not a concern, use an object instead of an array. Watch out for key clashes, though.
var dataList = {};
function addNewData(newData) {
dataList[newData.code] = newData;
dataList[newData.dataValue] = newData;
}
// that's it, no other changes necessary
If key clashes can occur - or ordered iteration is necessary, or if you just want to make it particularly clean, use an array and an accompanying index object.
var dataList = [];
var dataIndex = {
byCode: {},
byValue: {}
};
function addNewData(newData) {
dataList.push(newData);
dataIndex.byCode[newData.code] = newData;
dataIndex.byValue[newData.dataValue] = newData;
}
Here is my try using Proxies
// Code goes here
function DataInformation(code, dataValue) {
this.code = code;
this.dataValue = dataValue;
}
var _dataList = [];
var dataList = new Proxy(_dataList, {
get: function(target, name) {
if (target && target.myMap && target.myMap[name]) return target[target.myMap[name]];
return target[name];
},
set: function(obj, prop, value) {
// The default behavior to store the value
obj.myMap = obj.myMap || {};
obj.myMap[value.code] = prop;
obj[prop] = value;
return true;
}
});
function fillDataList() {
addNewData(new DataInformation("C1", 111));
addNewData(new DataInformation("C2", 222));
addNewData(new DataInformation("C3", 333));
}
function addNewData(newData) {
dataList.push(newData);
}
fillDataList();
console.log(dataList[0].dataValue);
console.log(dataList["C1"].dataValue);

Return array of functions as an array of booleans

So I'm working on updating some old projects and I am trying to find a source or an example of something I'm trying to accomplish.
what I have
// sample object of functions
var functions = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
Above is a sample of functions in an object. What I want to know is can the following be done:
pseudo code
//user input
var funcs = [required(''), isObject({key : 'v'})];
// what the function I'm building will process, in a sense
functions[funcs[i]](//arguments from funcs[i]);
// what would remain after each function
funcs = [false, true] // with an end result of false
I'm not 100% sure that this can't be done, I'm just not sure how in the slightest something like this would be able to come about. Let's bounce some ideas around here and see what we come up with.
Let me know if you all need any clarification of anything I asked. Thank you ahead of time for all help!
clarification on what I am trying to achieve
The object of functions is not finite, there can be any amount of functions for this specific program I am writing. They are going to be predefined, so user input is not going to be an issue. I need to be able to determine what function is called when it is passed, and make sure any arguments passed with said function are present and passed as well. So when I pass required(''), I need to be able to go through my object of functions and find functions['required'] and passed the empty string value with it. So like this functions['required']('').
other issues
The functions object is private access and the user won't have direct access to it.
How about this.
var functions = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
// Because these values are user inputs, they should be strings,
// so I enclosed them in quotes.
var funcs = ["required('')", "isObject({key: 'v'})"];
funcs.map(function(e) {
return eval('functions.' + e);
});
Running this should gives you an array of return values from the functions in the object.
Trivially, this could be done with:
var tests = [functions.required(''), functions.isObject({key: 'v'})];
If that's all you need, consider that my answer.
For a more general approach, the right tool here seems to be Arrays.prototype.map(). However, since you have an object containing all your functions instead of an array of functions, you'll need some way to make the correspondence. You can easily do this with a separate array of property names (e.g., ['required', 'isObject']). Then you could do something like this:
var functions = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
var args = ['', {key: 'v'}];
var results = ['required', 'isObject'].map(
function(test, i) {
return functions[test](args[i]);
}
);
Of course, if functions were an array instead of an object, you could simplify this:
var functions = [
/* required : */ function(value){
return value.length > 0;
},
/* isObject : */ function(value){
return typeof value == 'object';
}
];
var args = ['', {key: 'v'}];
var results = functions.map(
function(test, i) {
return test(args[i]);
}
);
If you wanted to encapsulate this a bit, you could pass the args array as a second argument to map(), in which case inside the function you would use this[i] instead of args[i].
Sure it's possible. Something like this:
var results = [];
var value = "value_to_pass_in";
for(var f in functions)
{
results.push(f.call(this, value));
}
UPDATE:
function superFunc(value)
{
var results = [];
for(var f in functions)
{
results.push(f.call(this, value));
}
return results;
}
superFunc("value_to_pass_in");
What you want is a map function. You can mimic it like this (I guess if you want one line):
https://jsfiddle.net/khoorooslary/88gh2yeh/
var inOneLine = (function() {
var resp = {};
var i = 0;
var fns = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
for (var k in fns) resp[k] = fns[k](arguments[i++]);
return resp;
}).apply(null, [ '' , {key : 'v'}]);
console.log(inOneLine);
var functions = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
var funcs = ["required('')", "isObject({key: 'v'})"];
function f(funcs){
return funcs.map(function(e) {
return eval('functions.' + e);
});
}
console.log(f(funcs));

Array that can store only one type of object?

Is it possible to create an array that will only allow objects of a certain to be stored in it? Is there a method that adds an element to the array I can override?
Yes you can, just override the push array of the array (let's say all you want to store are numbers than do the following:
var myArr = [];
myArr.push = function(){
for(var arg of arguments) {
if(arg.constructor == Number) Array.prototype.push.call(this, arg);
}
}
Simply change Number to whatever constructor you want to match. Also I would probably add and else statement or something, to throw an error if that's what you want.
UPDATE:
Using Object.observe (currently only available in chrome):
var myArr = [];
Array.observe(myArr, function(changes) {
for(var change of changes) {
if(change.type == "update") {
if(myArr[change.name].constructor !== Number) myArr.splice(change.name, 1);
} else if(change.type == 'splice') {
if(change.addedCount > 0) {
if(myArr[change.index].constructor !== Number) myArr.splice(change.index, 1);
}
}
}
});
Now in ES6 there are proxies which you should be able to do the following:
var myArr = new Proxy([], {
set(obj, prop, value) {
if(value.constructor !== Number) {
obj.splice(prop, 1);
}
//I belive thats it, there's probably more to it, yet because I don't use firefox or IE Technical preview I can't really tell you.
}
});
Not directly. But you can hide the array in a closure and only provide your custom API to access it:
var myArray = (function() {
var array = [];
return {
set: function(index, value) {
/* Check if value is allowed */
array[index] = value;
},
get: function(index) {
return array[index];
}
};
})();
Use it like
myArray.set(123, 'abc');
myArray.get(123); // 'abc' (assuming it was allowed)

Retrieving multiple items sequentially from chrome.storage

I'm writing an interface for user settings in a Chrome extension. Here's how I define the settings:
function Setting(label, type, defaultData) {
this.label = label;
this.type = 'checkbox';
this.defaultData = defaultData;
}
var Settings = {};
Settings['setting-one'] = new Setting('Setting one', 'checkbox', 'true');
Settings['setting-two'] = new Setting('Setting two', 'checkbox', 'true');
Here's how I set the default value for each setting:
function setDefaultSetting(setting, defaultValue) {
chrome.storage.sync.get(setting, function(results) {
if (!results[setting]) {
// If the setting has not yet been defined, define it now
var dataToSave = {};
dataToSave[setting] = defaultValue;
chrome.storage.sync.set(dataToSave, function() {
debugMsg('set `' + setting + '` to ' + defaultValue);
});
}
});
}
for (var setting in Settings) {
if (Settings.hasOwnProperty(setting)) {
var s = Settings[setting];
if (s.type == 'checkbox') {
setDefaultSetting(setting, s.defaultData);
}
}
}
So far, so good. Now I want to print the list of settings as checkboxes. Here's what I've tried:
function printSettingsModal() {
var output += '<form>';
for (var setting in Settings) {
if (Settings.hasOwnProperty(setting)) {
var s = Settings[setting];
if (s.type == 'checkbox') {
chrome.storage.sync.get(setting, function(results) {
output += '<p><input id="setting-' + setting + '" type="checkbox"';
if (results[setting] == 'true') { output += ' checked="checked"'; }
output += '><label for="setting-' + setting + '">' + s.name + '</label></p>';
});
}
}
}
output += '</form>';
return output;
}
This doesn't work because chrome.storage.sync.get() is asynchronous. How can I loop through my array of settings and retrieve the associated chrome.storage data for each setting?
You don't actually have to do that sequentially, or even set defaults explicitly beforehand.
chrome.storage.local.get has 3 forms for its first argument:
"key" (string): retrieves just one key, as you're doing
["key1", "key2"] (array): retrieves all the values for keys in the array
{ key1: default1, key2: default2 } (dictionary object): retrieves all the values for keys specified, returning the provided default value if it's not in storage
You can collect all the settings you want to retrieve and get them in one operation.
A simple way to do it is to use a recursive function, add each option inside the callback of the chrome.storage.sync.get() method, and call the printSettingsModal() function increasing the index. To do this you'll need an array structure to at least store the name of the settings so you can iterate over it, you can edit your Settings object and add the method add() that will do this for you. You can then call the chrome.storage.sync.get() method recursively and call a callback when finished.
So, first, here is your new Settings object:
function Setting(label, type, defaultData) {
this.label = label;
this.type = 'checkbox';
this.defaultData = defaultData;
}
function Settings() {
var _this = this;
this.list = [];
this.add = function(key, obj) {
this.list.push(key);
_this[key] = obj;
}
}
var mySettings = new Settings();
mySettings.add('setting-one', new Setting('Setting one', 'checkbox', 'true'));
mySettings.add('setting-two', new Setting('Setting two', 'checkbox', 'true'));
Now you will have something like this:
console.log(mySettings);
Settings {list: Array[2], add: function, setting-one: Setting, setting-two: Setting}
Here comes the recursive function, you will need to:
Initialize the output as "<form>" at the beginning.
Add all the settings in the intermediate steps.
Finalize the output adding "</form>" at the end.
Call a callback with the output so you can use it properly.
Here is an example of what your function will look like using recursion:
function printSettingsModal(index, output, callback) {
var s = mySettings[mySettings.list[index]];
if (index === 0) {
// first element is going to be created
output = "<form>";
} else if (index > mySettings.list.length-1) {
// the last setting has been added
// call the callback
callback(options);
}
if (s.type == 'checkbox') {
chrome.storage.sync.get(setting, function(results) {
output += '<p><input id="setting-' + setting + '" type="checkbox"';
if (results[setting] == 'true') { output += ' checked="checked"'; }
output += '><label for="setting-' + setting + '">' + s.name + '</label></p>';
if (index === mySettings.list.length-1) {
// last element has been created
output += '</form>';
printSettingsModal(++index, output, callback);
}
});
}
}
Now you just need to define a callback and call it:
function myCallback(options) {
// do something with the option string
// like:
document.getElementById('form-container').innerHTML = options;
}
printSettingsModal(0, null, myCallback);
If I wrote all correctly, this will work for you. Hope it helped.

Categories