Is it possible to sort without case-sensitivity?
For instance, sorts by default show up like this:
Awesomeman
adam
beyonce
but, I'd like to sort like:
adam
Awesomeman
beyonce
Is it possible to override the sensitivity easily? From what I can tell the grid inherits from OnDemandGrid and OnDemandList, which both inherit from Grid and List. For my store, I am using Memory wrapped in Observable.
As of now, I'm trying to overwrite _setSort in List.js, however that's not working. Anyone out there familiar with these frameworks?
There are potentially 2 ways of solving this:
On the grid end, by handling and canceling the dgrid-sort event
On the store end, by extending query to coerce sort into doing what you want (preferred)
First, the dgrid-sort version:
grid.on('dgrid-sort', function (event) {
// Cancel the event to prevent dgrid's default behavior which simply
// passes the sort criterion through to the store and updates the UI
event.preventDefault();
// sort is an array as expected by the store API, but dgrid's UI only sorts one field at a time
var sort = event.sort[0];
grid.set('sort', function (a, b) {
var aValue = a[sort.attribute].toLowerCase();
var bValue = b[sort.attribute].toLowerCase();
if (aValue === bValue) {
return 0;
}
var result = aValue > bValue ? 1 : -1;
return result * (sort.descending ? -1 : 1);
});
// Since we're canceling the event, we need to update the UI ourselves;
// the `true` tells it to also update dgrid's internal representation
// of the sort setting, so that toggling between asc/desc will still work
grid.updateSortArrow(event.sort, true);
});
While this works for handling when the user clicks in header cells, it will not take effect for programmatic set('sort') calls, or the initial setting of sort in the object passed to the Grid constructor, which could be problematic.
Since sorting is ultimately a store concern, addressing it on the store end is really the preferable solution. Admittedly dojo/store/Memory and namely dojo/store/util/SimpleQueryEngine doesn't make this...well...simple... but one thing to note about SimpleQueryEngine is that if you pass a function via queryOptions.sort rather than an array, it will be applied verbatim as the sort function to use.
This means we can take the incoming sort array that dgrid will set, write our own version of SimpleQueryEngine's default sort function while also accounting for case-insensitivity, and store that in queryOptions.sort for the inherited call:
var CIMemory = declare(Memory, {
query: function (query, queryOptions) {
var sort = queryOptions && queryOptions.sort;
if (sort) {
// Replace sort array with a function equivalent that performs
// case-insensitive sorting
queryOptions.sort = function (a, b) {
for (var i = 0; i < sort.length; i++) {
var aValue = a[sort[i].attribute].toLowerCase();
var bValue = b[sort[i].attribute].toLowerCase();
if (aValue !== bValue) {
var result = aValue > bValue ? 1 : -1;
return result * (sort[i].descending ? -1 : 1);
}
}
return 0;
}
}
return this.inherited(arguments);
}
});
Using this in place of dojo/store/Memory will cause all sorts to be case-insensitive.
Note that I took a couple of shortcuts over SimpleQueryEngine's sort function (checking for null/undefined and coercing values to primitives). Alter the sort function as necessary if you need to worry about either of those things.
Okay, for lack of a better solution, I found that I had to create a custom version of dojo's SimpleQueryEngine, adding .toLowerCase() on both of the values here. The reason it couldn't be simply changed was because it happens inside of an internal function (inside of another function) so it was easier to make another version entirely.
Then, when creating Memory, passing in the query engine like so:
new Memory({ queryEngine : customEngine });
and it seems to work. If there's a cleaner solution please share bc I hate this one :)
Related
I have this object, a 3rd party tracking tool similar to google analytics. I want to extend it with my own "caching" function that saves the data from the previous tracking call so that I can reference stuff on the next tracking call if needed.
This is what I have so far, and it works:
// Current 3rd party tool, can't really mess with this.
// It is loaded from an external script
window.someTool={/* stuff */};
// my code
someTool._cache=someTool._cache||{};
someTool._cache._get=function(variabl) {
var length,index,variabl=(variabl||'').split('.'),
cache=someTool&&someTool._cache&&someTool._cache._dataLayer||{};
for (index=0,length=var.length;index<length;index++){
cache=cache[variabl[index]];
if (!cache) break;
}
return cache;
};
So then I have/do the following
// data layer output on initial page that gets wiped later
var dataLayer = {
'page' : {
'name' : 'foo',
'lang' : 'en'
},
'events' : {
'pageView' : true,
'search' : true
}
}
// I grab the initial data layer and save it here
someTool._cache._dataLayer = dataLayer;
This then allows me to do stuff like
someTool._cache._get('page'); // returns {'page':{'name':'foo','lang':'en'}
someTool._cache._get('page')['name']; // returns 'foo'
someTool._cache._get('page.lang'); // returns 'en'
So this works for me, but here comes the question/goal: I want to improve my _get function. Namely, I don't like that I have to hardcode someTool, or really even _cache, and if I can somehow swing it, _dataLayer.
Ideally, I'd like a reference of someTool._cache._dataLayer passed/exposed to _get (e.g. a parent type reference) so that if someTool,_cache, or _dataLayer were to change namespaces, I don't have to update _get. But I am not sure how to do that.
This is what I have so far:
(function(tool, cache, dataLayer) {
var tool = tool || {},
cache = cache || '_cache',
dataLayer = dataLayer || '_dataLayer';
dataLayer = tool[cache][dataLayer] || {};
tool[cache]._get = function(property) {
var length, index, property = (property || '').split('.');
for (index = 0, length = property.length; index < length; index++) {
dataLayer = dataLayer[property[index]];
if (!dataLayer) break;
}
return dataLayer;
};
})(someTool, '_cache', '_dataLayer');
This seems to work the first time I call it, e.g.
someTool._cache._get('page')['name']; // returns 'foo'
But after that, I get an error:
TypeError: someTool._cache._get(...) is undefined
I feel like it has something to do with dataLayer losing its reference or something, I dunno (though I'm not sure how it's working first time around..). Is what I am doing even possible, and if so, where am I going wrong? Or is what I originally have the best I can do?
I feel like it has something to do with dataLayer losing its reference or something, I dunno (though I'm not sure how it's working first time around..).
The reason this is happening is because you are using the same dataLayer you initialize in the closure of _get to:
store information, and
to use as a temporary loop variable
If you look at your code:
(function(tool, cache, dataLayer) {
// ...
// Here you are initializing (or looking up) the dataLayer
dataLayer = tool[cache][dataLayer] || {};
tool[cache]._get = function(property) {
// ...
for (index = 0, length = property.length; index < length; index++) {
// here you are overwriting the same dataLayer
dataLayer = dataLayer[property[index]];
if (!dataLayer) break;
}
return dataLayer;
};
})(someTool, '_cache', '_dataLayer');
You can see that your loop will overwrite dataLayer on each iteration which means every lookup after the first will most likely be wrong.
Eventually, dataLayer will be overwritten with undefined, and then any further lookups will now break the code.
What you can do is use another variable for the loop iteration:
var temp;
for (index = 0, length = property.length; index < length; index++) {
temp = dataLayer[property[index]];
if (!temp) break;
}
return temp;
This will leave your dataLayer object intact.
Although your code is so obsfucated (one-character variable names, abuse of the comma operator, etc.) that its hard to tell for sure, it seems that you need to fix a few things before moving on.
Properties prefixed with an underscore are meant to be private. They are subject to change, and by change I mean your app randomly breaking. Use the public API.
Parsing strings out by hand is a lot of work for seemingly little gain. Is the use case for get('page.id') over get('page').id really so compelling?
Your code is incomprehensible. This is the kind of output one would expect of a minifier: it makes it hard to understand what any of it does/is supposed to do.
Unless a third-party API is so integral to your application that replacing it would require a rewrite no matter what (e.g. google maps) or so well-known that it has umpteen clones (jquery), its is generally a good idea to wrap third-party library calls so you can change the library later.
I realize this does not answer your question, but its way too long for a comment and it would be remiss of me to not point out the bright red targets (plural) you've painted on your feet prior to polishing your firearm.
As for your actual question (post-edit), you're on the right track. But I'd make it a curried function so that you can dynamically access different properties. We're going to ignore for one minute the huge mistake that is accessing private properties just to get the point across:
function accessDataCache(cache) {
return function(dataLayer) {
return function(namespaceObj) {
return function(property) {
return namespaceObj[cache][dataLayer][property];
};
};
};
};
var getFn = accessDataCache('_cache')('_dataLayer')(someTool);
getFn('page');
You can now also mix and match if you need other stuff:
var getSomeOtherCachedThing = accessDataCache('_cache')('_someOtherThing')(someTool);
All of that is quite tedious to write out by hand, so I recommend using something like lodash or Ramda and .curry to achieve the effect:
var accessCacheData = R.curry(function(cache, dataLayer, namespaceObj, property) {
return namespaceObj[cache][dataLayer][property];
});
I have a function that receives a list of JS objects as an argument. I need to store information about those objects in a private variable for future reference. I do not want to stuff a property into the objects themselves, I just want to keep it out of band in a dictionary. I need to be able to lookup metadata for an object in sub-linear time.
For this I need a hash function such that, for any two objects o1 and o2,
hash(o1) !== hash(o2) whenever o1 !== o2.
A perfect example of such a hash function would be the memory address of the object, but I don't think JS exposes that. Is there a way?
Each object reference is different. Why not push the object onto an array? Traversing the array looking for an object reference might still perform better than inspecting each object in a recursive manor to generate a hash key.
function Dictionary() {
var values = [];
function contains(x) {
var i = values.length;
while(i--) {
if (values[i] === x) {
return true;
}
}
return false;
}
function count() {
return values.length;
}
function get(i) {
return (i >= 0 && i < values.length) ? values[i] : null;
}
function set(o) {
if (contains(o)) {
throw new Error("Object already exists in the Dictionary");
}
else {
return values.push(o) - 1;
}
}
function forEach(callback, context) {
for (var i = 0, length = values.length; i < length; i++) {
if (callback.call(context, values[i], i, values) === false) {
break;
}
}
}
return {
get: get,
set: set,
contains: contains,
forEach: forEach,
count: count
};
}
And to use it:
var objects = Dictionary();
var key = objects.set({});
var o = objects.get(key);
objects.contains(key); // returns true
objects.forEach(function(obj, key, values) {
// do stuff
}, this);
objects.count(); // returns 1
objects.set(o); // throws an error
To store metadata about objects, you can use an WeakMap:
WeakMaps are key/value maps in which keys are objects.
Note that this API is still experimental and thus not widely supported yet (see support table). There is a polyfill implementation which makes use of defineProperty to set GUIDs (see details here).
Javascript does not provide direct access to memory (or to the file system for that matter).
You'd probably just want to create your properties/variables within the analysis (hash) function, and then return them to where the function was called from to be stored/persisted for later reference.
Thanks everyone who chipped in to reply. You all have convinced me that what I want to do is currently not possible in JavaScript.
There seem to be two basic compromises that someone with this use case can chose between:
Linear search using ===
=== appears to be the only built-in way to distinguish between two identically-valued objects that have different references. (If you had two objects, o1 and o2, and did a deep comparison and discovered that they were value-identical, you might still want to know if they're reference-identical. Besides === you could do something weird like add a property to o1 and see if showed up in o2).
Add a property to the object.
I didn't like this approach because there's no good reason why I should have to expose this information to the outside world. However, a colleague tipped me off to a feature that I didn't know about: Object.defineProperty. With this, I can alleviate my main concerns: first, that my id would show up, unwanted, during object enumeration, and second, that someone could inadvertently alter my id if there were to be a namespace collision.
So, in case anyone comes here wanting the same thing I wanted, I'm putting it up there for the record that I'm going to add a unique id using Object.defineProperty.
var obj = {};
obj.a = 1; // fire event, property "a" added
This question is different from this one, where ways to detect when an already declared property is changed, being discussed.
this is possible, technically, but since all current JS implementations that I know of are single threaded it won't be very elegant. The only thing I can think of is a brute force interval:
var checkObj = (function(watchObj)
{
var initialMap = {},allProps = [],prop;
for (prop in watchObj)
{
if (watchObj.hasOwnProperty(prop))
{//make tracer object: basically clone it
initialMap[prop] = watchObj[prop];
allProps.push(prop);//keep an array mapper
}
}
return function()
{
var currentProps = [];
for (prop in watchObj)
{
if (watchObj.hasOwnProperty(prop))
{//iterate the object again, compare
if (watchObj[prop] !== initialMap[prop])
{//type andvalue check!
console.log(initialMap[prop] + ' => ' watchObj[prop]);
//diff found, deal with it whichever way you see fit
}
currentProps.push(prop);
}
}
//we're not done yet!
if (currentProps.length < allProps.length)
{
console.log('some prop was deleted');
//loop through arrays to find out which one
}
};
})(someObjectToTrack);
var watchInterval = setInterval(checkObj,100);//check every .1 seconds?
That allows you to track an object to some extent, but again, it's quite a lot of work to do this 10/sec. Who knows, maybe the object changes several times in between the intervals, too.All in all, I feel as though this is a less-then-ideal approach... perhaps it would be easier to compare the string constants of the JSON.stringify'ed object, but that does mean missing out on functions, and (though I filtered them out in this example) prototype properties.
I have considered doing something similar at one point, but ended up just using my event handlers that changed the object in question to check for any changes.
Alternatively, you could also try creating a DOMElement, and attach an onchange listener to that... sadly, again, functions/methods might prove tricky to track, but at least it won't slow your script down as much as the code above will.
You could count the properties on the object and see if has changed from when you last checked:
How to efficiently count the number of keys/properties of an object in JavaScript?
this is a crude workaround, to use in case you can't find a proper support for the feature in the language.
If performance matters and you are in control of the code that changes the objects, create a control class that modifies your objects for you, e.g.
var myObj = new ObjectController({});
myObj.set('field', {});
myObj.set('field.arr', [{hello: true}]);
myObj.set('field.arr.0.hello', false);
var obj = myObj.get('field'); // obj === {field: {arr: [{hello: false}]}}
In your set() method, you now have the ability to see where every change occurs in a pretty high-performance fashion, compared with setting an interval and doing regular scans to check for changes.
I do something similar but highly optimised in ForerunnerDB. When you do CRUD operations on the database, change events are fired for specific field paths, allowing data-bound views to be updated when their underlying data changes.
According to my research and googling, Javascript seems to lack support for locale aware sorting and string comparisons. There is localeCompare(), but it has been reported of browser specific differencies and impossibility to explicitly set which locale is used (the OS locale is not always the one wanted). There is some intentions to add collation support inside ECMAScript, but before it, we are on our own. And depending how consistent the results are across browsers, may be we are on our own forever :(.
I have the following code, which makes alphabetical sort of an array. It's made speed in mind, and the ideas are got from https://stackoverflow.com/a/11598969/1691517, to which I made some speed improvements.
In this example, the words array has 13 members and the sort-function is called 34 times. I want to replace some of the letters in the words-array (you don't have to know what replacements are made, because it's not the point in this question). If I make these replacements in sort-function ( the one that starts with return function(a, b) ), the code is inefficient, because replacements are made more than once per array member. Of course I can make these replacements outside of this closure, I mean before the line words.sort(sortbyalphabet_timo);, but it's not what I want.
Question 1: Is it possible to modify the words-array in between the lines "PREPARATION STARTS" and "PREPARATION ENDS" so that the sort function uses modified words-array?
Question 2: Is it possible to input arguments to the closure so that code between PREPARATION STARTS and PREPARATION ENDS can use them? I have tried this without success:
var caseinsensitive = true;
words.sort( sortbyalphabet_timo(caseinsensitive) );
And here is finally the code example, and the ready to run example is in http://jsfiddle.net/3E7wb/:
var sortbyalphabet_timo = (function() {
// PREPARATION STARTS
var i, alphabet = "-0123456789AaÀàÁáÂâÃãÄäBbCcÇçDdEeÈèÉéÊêËëFfGgHhIiÌìÍíÎîÏïJjKkLlMmNnÑñOoÒòÓóÔôÕõÖöPpQqRrSsTtUuÙùÚúÛûÜüVvWwXxYyÝýŸÿZz",
index = {};
i = alphabet.length;
while (i--) index[alphabet.charCodeAt(i)] = i;
// PREPARATION ENDS
return function(a, b) {
var i, len, diff;
if (typeof a === "string" && typeof b === "string") {
(a.length > b.length) ? len = a.length : len = b.length;
for (i = 0; i < len; i++) {
diff = index[a.charCodeAt(i)] - index[b.charCodeAt(i)];
if (diff !== 0) {
return diff;
}
}
// sort the shorter first
return a.length - b.length;
} else {
return 0;
}
};
})();
var words = ['tauschen', '66', '55', '33', 'täuschen', 'andern', 'ändern', 'Ast', 'Äste', 'dosen', 'dösen', 'Donaudam-0', 'Donaudam-1'];
$('#orig').html(words.toString());
words.sort(sortbyalphabet_timo);
$('#sorted').html(words.toString());`
Is it possible to modify the words-array in between the lines "PREPARATION STARTS" and "PREPARATION ENDS" so that the sort function uses modified words-array?
No, not really. You don't have access to the array itself, your function only builds the compare-function that is later used when .sort is invoked on the array. If you needed to alter the array, you'll need to write a function that gets it as an argument; for example you could add a method on Array.prototype. It would look like
function mysort(arr) {
// Preparation
// declaration of compare function
// OR execution of closure to get the compare function
arr.sort(comparefn);
return arr;
}
Is it possible to input arguments to the closure so that code between PREPARATION STARTS and PREPARATION ENDS can use them?
Yes, of course - that is the reason to use closures :-) However, you can't use sortbyalphabet_timo(caseinsensitive) with your current code. The closure you have is immediately invoked (called an IIFE) and returns the compare-function, which you pass into sort as in your demo.
If you want sortbyalphabet_timo to be the closure instead of the result, you have to remove the brackets after it. You also you can use arguments there, which are accessible in the whole closure scope (including the comparefunction):
var sortbyalphabet_timo_closure = function(caseinsensitive) {
// Preparation, potentially using the arguments
// Declaration of compare function, potentially using the arguments
return comparefn;
}
// then use
words.sort(sortbyalphabet_timo_closure(true));
Currently, you are doing this:
var sortbyalphabet_timo_closure = function(/*having no arguments*/) {
// Preparation, potentially using the arguments
// Declaration of compare function, potentially using the arguments
return comparefn;
}
var sortbyalphabet_timo = sortbyalphabet_timo_closure();
// then use
words.sort(sortbyalphabet_timo);
…which just caches the result of executing the closure, if you'd need to sort multiple times.
I've made this wonderful plugin: https://github.com/suprMax/Zepto-onPress
Which works perfectly besides one little detail. When I get callback function I need to store it alongside with my real event handler so I can detach it when someone tries to remove event handler and provide me the original callback. So basically I need to be able to store multiple key-value pairs per element where the key supposed to be a function and value is a function too. And I tried to do just that, but right now the script makes this internally:
(function(){}).toString()
Which is not a best idea since I can remove wrong event handlers because of :
(function(){}).toString() === (function(){}).toString().
I suppose there is a better way to do just that. Any suggestions are very welcome.
The only safe and performant way is to store your functions in an array (indexOf works perfectly as it'll search on the pointer values) and use the array index to push the other method to another one.
var fn1store = [];
var fn2store = [];
function pushFunctions(fn1, fn2) {
var p;
if (p = fn1store.indexOf(fn1) > -1) {
teardown(fn2store[p]);
}
p = fn1store.push(fn1) -1;
fn2store[p] = fn2;
}