I've declared Javascript arrays in such a way that I could then access them by a key, but it was a long time ago, and I've forgotten how I did it.
Basically, I have two fields I want to store, a unique key, and its value. I know there is a way to do it.. something like:
var jsArray = new {key: 'test test', value: 'value value'},
new {key: 'test 2', value: 'value 2'};
and accessed like:
value = jsArray[key]
Can someone remind me?
You can do it in different ways:
var a = {'a':0, 'b':1, 'c':2};
var b = new Array();
b['a'] = 0;
b['b'] = 1;
b['c'] = 2;
var c = new Object();
c.a = 0;
c.b = 1;
c.c = 2;
var myFancyDictionary = {
key: 'value',
anotherKey: 'anotherValue',
youGet: 'the idea'
}
If you are already using Prototype, try using its Hash. If using jQuery, try using Map.
Here is a JavaScript class that provides a simple dictionary.
if( typeof( rp ) == "undefined" ) rp = {};
rp.clientState = new function()
{
this.items = new Object();
this.length = 0;
this.set = function( key, value )
{
if ( ! this.keyExists( key ) )
{
this.length++;
}
this.items[ key ] = value;
}
this.get = function( key )
{
if ( this.keyExists( key ) )
{
return this.items[ key ];
}
}
this.keyExists = function( key )
{
return typeof( this.items[ key ] ) != "undefined";
}
this.remove = function( key )
{
if ( this.keyExists( key ) )
{
delete this.items[ key ];
this.length--;
return true;
}
return false;
}
this.removeAll = function()
{
this.items = null;
this.items = new Object();
this.length = 0;
}
}
Example use:
// Add a value pair.
rp.clientState.set( key, value );
// Fetch a value.
var x = rp.clientState.Get( key );
// Check to see if a key exists.
if ( rp.clientState.keyExists( key )
{
// Do something.
}
// Remove a key.
rp.clientState.remove( key );
// Remove all keys.
rp.clientState.removeAll();
Related
There is a complex object and based on an array which is given as an input I need to modify its properties. Illustration is shown below. If the "field" is same , add them to "or" array .If its different "field" add them to "and" array along with its "value". I am using Set to get keys from both source and input and using them to group based on its keys. Also whenever there are duplicates .ie., suppose the "filterObj" already has the same (field, value) pair. Be it in "and" or inside "or",Then don't add it in the final object
Sandbox: https://codesandbox.io/s/optimistic-mirzakhani-pogpw-so-dpvis
There is a TestCases file in the sandbox which its needs to pass
let filterObj = {
feature: "test",
filter: {
and: [{ field: "field2" }]
}
};
let obj = [{ field: "field2", value: "3" }];
let all_filters = [];
if (filterObj.filter.and && filterObj.filter.and.hasOwnProperty("or")) {
all_filters = [...filterObj.filter.and.or];
} else if (filterObj.filter.and) {
all_filters = [...filterObj.filter.and];
}
const all_objs = [...obj, ...all_filters];
const uniqKeys = all_objs.reduce(
(acc, curr) => [...new Set([...acc, curr.field])],
[]
);
const updateItems = uniqKeys.map(obj => {
const filter_items = all_objs.filter(item => item.field === obj);
let resultObj = {};
if (filter_items && filter_items.length > 1) {
resultObj.or = [...filter_items];
} else if (filter_items && filter_items.length === 1) {
resultObj = { ...filter_items[0] };
}
return resultObj;
});
var result = { ...filterObj, filter: { and: [...updateItems] } };
console.log(result);
Try it.
I redid the implementation, it happened more universally.
Parses any filters according to your algorithm that it finds.
All test cases are worked.
Sandbox link: https://codesandbox.io/s/optimistic-mirzakhani-pogpw-so-i1u6h
let filterObj = {
feature: "test",
filter: {
and: [
{
field: "field1",
value: "2"
}
]
}
};
let obj = [
{
field: "field1",
value: "2"
},
{
field: "field1",
value: "1"
}
];
var FilterController = function(filter) {
var self = this;
self.filter = filter;
// encapsulated map of objects by fields
var storeMap = {};
// counter of objects
var counter = 0;
var tryPutObjectToMap = function(object) {
if (typeof object === "object") {
// get type for grouping
var objectType = self.getObjectGroupType(object);
if (objectType !== null) {
// cheack have group
if (!storeMap.hasOwnProperty(objectType)) {
storeMap[objectType] = [];
}
var duplicate = storeMap[objectType].find(function(sObject) {
return self.getObjectValue(sObject) === self.getObjectValue(object);
});
// check duplicate
if (duplicate === undefined) {
counter++;
storeMap[objectType].push(object);
} else {
// TODO: Handle duplicates
}
} else {
// TODO: handle incorrect object
}
}
};
// get filter structure from map
var getFilterStructureFromMap = function() {
var result = {};
// check exists root filter and filed if have objects
if (counter > 0) {
result["and"] = [];
}
for (var key in storeMap) {
if (storeMap.hasOwnProperty(key)) {
var array = storeMap[key];
if (array.length > 1) {
result["and"].push({
// clone array
or: array.slice()
});
} else {
result["and"].push(array[0]);
}
}
}
return result;
};
// rewrite and get current filter
// if you need^ create new object for result
self.rewriteAndGetFilter = function() {
self.filter.filter = getFilterStructureFromMap();
return self.filter;
};
// not prototype function for have access to storeMap
self.putObjects = function(objects) {
if (Array.isArray(objects)) {
// recursive push array elements
objects.forEach(element => self.putObjects(element));
// handle array
} else if (typeof objects === "object") {
// handle object
if (objects.hasOwnProperty("and") || objects.hasOwnProperty("or")) {
for (var key in objects) {
//no matter `or` or `and` the same grouping by field
// inner object field
if (objects.hasOwnProperty(key)) {
self.putObjects(objects[key]);
}
}
} else {
// filters props not found, try push to store map
tryPutObjectToMap(objects);
}
} else {
// TODO: Handle errors
}
};
if (self.filter.hasOwnProperty("filter")) {
// put and parse current objects from filter
self.putObjects(self.filter.filter);
}
};
// function for grouping objects.
// for you get filed name from object.
// change if need other ways to compare objects.
FilterController.prototype.getObjectGroupType = function(obj) {
if (typeof obj === "object" && obj.hasOwnProperty("field")) {
return obj.field;
}
return null;
};
// get object value
FilterController.prototype.getObjectValue = function(obj) {
if (typeof obj === "object" && obj.hasOwnProperty("value")) {
return obj.value;
}
return null;
};
var ctrl = new FilterController(filterObj);
ctrl.putObjects(obj);
var totalFilter = ctrl.rewriteAndGetFilter();
console.log(totalFilter);
console.log(JSON.stringify(totalFilter));
EDIT 1
I did not change the logic; I made a function based on it.
let filterObj = {
feature: "test",
filter: {
and: [
{
field: "field1",
value: "2"
}
]
}
};
let obj = [
{
field: "field1",
value: 2
},
{
field: "field1",
value: "1"
}
];
function appendToFilter(filter, inputObjects) {
var storeMap = {};
var counter = 0;
var handlingQueue = [];
// if filter isset the appen to handling queue
if (filter.hasOwnProperty("filter")) {
handlingQueue.push(filter.filter);
}
// append other object to queue
handlingQueue.push(inputObjects);
// get first and remove from queue
var currentObject = handlingQueue.shift();
while (currentObject !== undefined) {
if (Array.isArray(currentObject)) {
currentObject.forEach(element => handlingQueue.push(element));
} else if (typeof currentObject === "object") {
if (currentObject.hasOwnProperty("and") || currentObject.hasOwnProperty("or")) {
for (var key in currentObject) {
if (currentObject.hasOwnProperty(key)) {
handlingQueue.push(currentObject[key]);
}
}
} else {
// TODO: append fild exists check
if (currentObject.field) {
if (!storeMap.hasOwnProperty(currentObject.field)) {
storeMap[currentObject.field] = [];
}
var localValue = currentObject.value;
// check duplicate
if (storeMap[currentObject.field].find(object => object.value === localValue) === undefined) {
counter++;
storeMap[currentObject.field].push(currentObject);
}
}
}
}
currentObject = handlingQueue.shift();
}
// create new filter settings
var newFilter = {};
// check exists root filter and filed if have objects
if (counter > 0) { newFilter["and"] = []; }
for (var storeKey in storeMap) {
if (storeMap.hasOwnProperty(storeKey)) {
var array = storeMap[storeKey];
if (array.length > 1) {
newFilter["and"].push({
// clone array
or: array.slice()
});
} else {
newFilter["and"].push(array[0]);
}
}
}
filter.filter = newFilter;
}
// update filterObj
appendToFilter(filterObj, obj);
console.log(filterObj);
EDIT 2,3 (UPDATED)
With others objects support.
export function appendToFilter(filter, inputObjects) {
var storeMap = {};
var others = [];
var counter = 0;
var handlingQueue = [];
// if filter isset the appen to handling queue
if (filter.hasOwnProperty("filter") && filter.filter.hasOwnProperty("and")) {
handlingQueue.push(filter.filter.and);
}
// append other object to queue
handlingQueue.push(inputObjects);
// get first and remove from queue
var currentObject = handlingQueue.shift();
while (currentObject !== undefined) {
if (Array.isArray(currentObject)) {
currentObject.forEach(element => handlingQueue.push(element));
} else if (typeof currentObject === "object") {
if (
currentObject.hasOwnProperty("and") ||
currentObject.hasOwnProperty("or")
) {
for (var key in currentObject) {
if (currentObject.hasOwnProperty(key)) {
handlingQueue.push(currentObject[key]);
}
}
} else {
// TODO: append fild exists check
if (currentObject.field) {
if (!storeMap.hasOwnProperty(currentObject.field)) {
storeMap[currentObject.field] = [];
}
var localValue = currentObject.value;
// check duplicate
if (
storeMap[currentObject.field].find(
object => object.value === localValue
) === undefined
) {
counter++;
storeMap[currentObject.field].push(currentObject);
}
} else {
// handle others objects^ without field "field"
counter++;
others.push(currentObject);
}
}
}
currentObject = handlingQueue.shift();
}
// create new filter settings
var newFilter = {};
// check exists root filter and filed if have objects
if (counter > 0) {
newFilter["and"] = [];
}
for (var storeKey in storeMap) {
if (storeMap.hasOwnProperty(storeKey)) {
var array = storeMap[storeKey];
if (array.length > 1) {
newFilter["and"].push({
// clone array
or: array.slice()
});
} else {
newFilter["and"].push(array[0]);
}
}
}
// Append others to result filter
others.forEach(other => newFilter["and"].push(other));
filter.filter = newFilter;
}
Hey guys I'm having a bit of trouble with this one, and I was hoping someone could give me a hand. I am trying to break up strings that follow this format foo.bar::baz into an object that resembles this foo.bar.baz = ''
In PHP I would do this with the following
$obj = New stdClass();
$inp = [
'type1',
'type2',
'type3.sub1::blah1',
'type3.sub2::blah1',
'type4.sub1::blah2',
'type4.sub2::blah2',
'type5.sub1',
];
foreach ($inp AS $v)
{
if (strpos($v, '.'))
{
$b = explode('.', $v);
$obj->$b[0] = '';
if (strpos($b[1], '::') && $c = explode('::', $b[1]))
{
$obj->$b[0]->$c[0]->$c[1] = '';
} else {
$obj->$b[0]->$b[1] = '';
}
} else {
$obj->$v = '';
}
}
print_r($obj);
stdClass Object
(
[type1] =>
[type2] =>
[type3] => stdClass Object
(
[sub2] => stdClass Object
(
[blah1] =>
)
)
[type4] => stdClass Object
(
[sub2] => stdClass Object
(
[blah2] =>
)
)
[type5] => stdClass Object
(
[sub1] =>
)
)
I am currently trying to mimic this in Javascript doing the following, but can't seem to get it to behave
var fieldset = results.fooresult.metadata[0].field;
var namespace = [];
// namespace from meta
for (var k in fieldset) {
if (fieldset.hasOwnProperty(k)) {
var string = fieldset[k]['$']['NAME'];
if (0<string.indexOf('.')) {
var pairs = string.split('.');
if (0<pairs[1].indexOf('::')) {
var value = pairs[1].split("::");
namespace[pairs[0]][value[0]] = value[1];
} else {
namespace[pairs[0]] = pairs[1];
}
} else {
namespace.push(string);
}
}
}
Help would be appreciated
This should work:
var inp = [
'type1',
'type2',
'type3.sub1::blah1',
'type3.sub2::blah1',
'type4.sub1::blah2',
'type4.sub2::blah2',
'type5.sub1'
];
function makeTree(inp) {
var result = {};
var split = inp.map(function (str) {
return str.split(/\.|::/);
});
var walk = function (obj, propList) {
if (propList.length == 1) {
obj[propList[0]] = '';
return;
}
var nextObj = obj[propList[0]] = obj[propList[0]] || {};
propList.shift();
walk(nextObj, propList);
};
split.forEach(walk.bind(null, result));
return result;
}
var out = makeTree(inp);
Here's my select2 component:
<select
multiple
id="e1"
placeholder: "Select meeting participants"
style="width:450px;"></select>
By default, when the page loads, an ajax call is made to load it with a user's contacts.
// Roster list remote call + handler =========================================>
transporter.ajax( "/transporter/app/roster/getAll", {},
function( data )
{
var contacts = data.response.rlist.contactList;
for( var i = 0; i < contacts.length; i++ )
{
var obj = new Object();
obj[ 'id' ] = i;
obj[ 'displayName' ] = contacts[i].displayName;
obj[ 'contactName' ] = contacts[i].contactName;
obj[ 'contactType' ] = contacts[i].contactType;
obj[ 'avatar' ] = contacts[i].avatar;
obj[ 'status' ] = contacts[i].status;
roster.push( obj );
}
var ddl = document.getElementById( 'e1' );
for ( var j = 0; j < roster.length; j++ )
{
var o = document.createElement( 'option' );
o.value = roster[ j ].contactName;
o.text = roster[ j ].displayName;
ddl.appendChild( o );
}
});
This is what I want to happen: Initial ajax call loads and displays a user's local contact list. It's nice for the user to see and easy for them to select people they often invite for mtgs.
The problem: If they want a user not in their contacts they see a "no matches found" message.
The question: Can the Select2 component then make an ajax call to search a larger directory (at a different endpoint)?
My guess is that it can't and I'll have to add a 2nd Select2 component to do this.
Anyone know the answer?
Thanks for any helpful tips!
Why bother with a second ajax call at all ?
/transporter/app/roster/getAll
If it returns "no users found" ..you could
Don't return "no users found", branch into a more complex search
right away "second DB like you said", and actually return something
if you do want a second ajax call just do something like this
transporter.ajax( "/transporter/app/roster/getAll", {}, callBack);
function callBack( data ) {
var contacts = data.response.rlist.contactList;
if (contacts == null || contacts.length == 0) {
// callback fillList, don't call this in an endless loop
transporter.ajax( "/transporter/app/roster/searchAll", {}, fillList);
return;
}
fillList( data );
}
function fillList( data ) {
var contacts = data.response.rlist.contactList;
for( var i = 0; i < contacts.length; i++ ) {
var obj = new Object();
obj[ 'id' ] = i;
obj[ 'displayName' ] = contacts[i].displayName;
obj[ 'contactName' ] = contacts[i].contactName;
obj[ 'contactType' ] = contacts[i].contactType;
obj[ 'avatar' ] = contacts[i].avatar;
obj[ 'status' ] = contacts[i].status;
roster.push( obj );
}
var ddl = document.getElementById( 'e1' );
for ( var j = 0; j < roster.length; j++ ) {
var o = document.createElement( 'option' );
o.value = roster[ j ].contactName;
o.text = roster[ j ].displayName;
ddl.appendChild( o );
}
}
I think you actually answered the question yourself
I'm using a query function to test against a local dataset before I make an ajax call to search the directory. Right now I don't have a good filtering function on the local dataset and I make the ajax call whenever the input isn't an empty String.
It's not ideal but it works for now.
$( '#e1' ).select2({
placeholder: "Select participants",
allowClear:true,
multiple: true,
query: function ( query )
{
if( query.term == '')
{
// Query roster list first, if no matches then query directory
transporter.ajax( "/transporter/app/roster/getAll", {},
function( data )
{
var contacts = data.response.rlist.contactList;
var d = { results: [] };
for( var i = 0; i < contacts.length; i++ )
{
var obj = new Object();
obj[ 'id' ] = i;
obj[ 'displayName' ] = contacts[i].displayName;
obj[ 'contactName' ] = contacts[i].contactName;
obj[ 'contactType' ] = contacts[i].contactType;
obj[ 'avatar' ] = contacts[i].avatar;
obj[ 'status' ] = contacts[i].status;
roster.push( obj );
d.results.push( { id:obj.contactName, displayName:obj.displayName, type:0 });
}
query.callback( d );
}
);
}else{
var params = {
query:query.term + '*',
searchFor:'users',
sortBy:'relevance'
};
// Query directory
transporter.ajax( "/transporter/app/search", params,
function( data )
{
if( data.response.userList != null )
{
if( data.response.userList.user != null )
{
var peeps = data.response.userList.user;
var datum = { results: [] };
for( var i = 0; i < peeps.length; i++ )
{
datum.results.push( { id:peeps[ i ].loginName, displayName:peeps[ i ].displayName, type:1 } );
}
query.callback( datum );
}else{
var noResults = { results: [] };
query.callback( noResults );
}
}
}
);
}
},
formatResult:formatContacts,
formatSelection:formatContactsSelection,
escapeMarkup: function( m ) { return m; }
});
function formatContacts( item ) {
if( item.type != 0 )
return item.displayName // + '<br/><a>Add User to Roster</a>';
else
return item.displayName;
};
function formatContactsSelection( item ) {
return item.displayName;
};
I'm trying to write a function that can look up a namespace and value in a JavaScript object and return the key.
Image this data:
var o = {
map: {
lat : -33.86749,
lng : 151.20699,
zoom : 12
},
filters : {
animals : {
dogs: {
myDog : 'fido'
}
}
}
};
function get(namespace, key){
//TODO
}
get('filters.animals.dogs', 'myDog')
How would you build a function that does this dynamically - no matter the depth of the namespace?
This function is somewhat close, only it modifies the original object which we don't want ofcourse:
var get = function(obj, namespace, key, value) {
var parts = namespace.split('.');
for(var i = 0; i < parts.length; i++) {
if(typeof obj[parts[i]] == 'undefined'){
obj[parts[i]] = {};
}
obj = obj[parts[i]];
}
return obj[key] = value;
};
Reason for the madness is that I cannot expose the object. It must remain private and a public method must spit out a result.
Give this a try.
function get(namespace, key) {
var parts = namespace.split('.'),
i = 0,
l = parts.length,
obj = o;
while ( i < l ) {
obj = obj[parts[i]];
if ( ! obj ) break;
i++;
}
return obj && obj[key] ? obj[key] : null;
}
I have created a fiddle with working code. Hope that helps.
http://jsfiddle.net/RdhJF/2/
var o = {
map: {
lat : -33.86749,
lng : 151.20699,
zoom : 12
},
filters : {
animals : {
dogs: {
myDog : 'fido'
}
}
}
};
function get(obj, namespace)
{
var parts = namespace.split('.');
if(parts.length==0)
return -1;
var previousValue = obj;
for(var i = 0; i < parts.length; i++)
{
if(typeof previousValue[parts[i]] == 'undefined')
return -1;
else
previousValue = previousValue[parts[i]];
}
return previousValue;
}
var myValue= get(o,'filters.animals.dogs.myDog');
alert(myValue);
I have a string like this "namespace.fun1.fun2.fun3" passed from the client. It's telling the server which function to use.
How do I safely run the function?
right now i'm doing:
var runthis = string.split('.')
namespace[runthis[1]][runthis[2]][runthis[3]]
How do I handle arbitrary depth of nesting safely?
A little function I wrote. I use it in most of my applications:
Object.lookup = (function _lookup() {
var cache = { };
return function _lookupClosure( lookup, failGracefully ) {
var check = null,
chain = [ ],
lastkey = '';
if( typeof lookup === 'string' ) {
if( cache[ lookup ] ) {
chain = cache[ lookup ].chain;
check = cache[ lookup ].check;
}
else {
lookup.split( /\./ ).forEach(function _forEach( key, index, arr ) {
if( check ) {
if( typeof check === 'object' ) {
if( key in check ) {
chain.push( check = check[ key ] );
lastkey = key;
}
else {
if( !failGracefully ) {
throw new TypeError( 'cannot resolve "' + key + '" in ' + lastkey );
}
}
}
else {
if( !failGracefully ) {
throw new TypeError( '"' + check + '" ' + ' does not seem to be an object' );
}
}
}
else {
lastkey = key;
chain.push( check = window[ key ] );
}
});
if( check ) {
cache[ lookup ] = {
chain: chain,
check: check
};
}
}
}
return {
execute: function _execute() {
return typeof check === 'function' ? check.apply( chain[chain.length - 2], arguments ) : null;
},
get: function _get() {
return check;
}
};
}
}());
usage:
Object.lookup( 'namespace.fun1.fun2.fun3' ).execute();
The first parameter is the object/method/property to resolve. The second (optional) parameter indicates whether or not the lookup() method shall fail silently or throw an exception if some property or object could not get resolved. default is 'throw'. To avoid that call
Object.lookup( 'namespace.fun1.fun2.fun3', true ).execute( 'with', 'paras' );
If .fun3 is a function, you can pass in any parameters into .execute() instead.
if you just want to receive the property value, you can also call .get() instead of .execute()
var val = Object.lookup( 'namespace.fun1.fun2.fun3' ).get();
(I may be misinterpreting the question, but this is what came to mind)
var s = "space.f.g.h.i.j.k.l.m",
a = s.split( "." ),
fn = eval( a[0] );
for ( var i = 1; i < a.length; i++ ) {
fn = fn[ a[i] ];
}
fn();
Note: this won't guard against the namespace being specified incorrectly or maliciously.
This should do:
var get = function(obj, key) {
var s = key.split('.')
, i = 1
, l = s.length;
for (; i < l; i++) {
obj = obj[s[i]];
if (!obj) return;
}
return obj;
};
get({hello:{world:{}}}, 'ns.hello.world');
edit: changed code a bit
Here's a simple for loop that should do find each object specified strating from the global scope, and then run the function it finds.
window.namespace = { fun1: { fun2: { fun3: function() { alert('hi!') }}}};
runFunc = function(address) {
var addressArray = address.split('.'),
current = window,
i = 0;
for (i = 0; i < addressArray.length; i++) {
current = current[addressArray[i]];
}
current();
};
runFunc('namespace.fun1.fun2.fun3');
http://jsfiddle.net/jfWra/1/
And here's some eror protection that will throw something meaningful if the value referenced doesnt exist or is not a function: http://jsfiddle.net/jfWra/2/
Here's another simple solution using a recursive function:
function run(str, context) {
var path = str.split(".")
if path.length == 1 {
context[path[0]].call()
return;
}
if(typeof context == 'undefined') {
context = window[path[0]]
} else {
context = context[path[0]]
}
run(path.splice(1).join('.'), context)
}
Two years later there is this module: https://github.com/daaku/nodejs-dotaccess.
var get = require("dotaccess").get;
var o = {a:{b:{c:'deep'}}};
console.log(get(o, 'a.b.c'));