I'm sorting array:
myArray.sort(comparators.some_comparator);
and I have several comparator to choose from:
comparators = {
asc_firstname_comparator : function(o1, o2){
...
}
desc_firstname_comparator : function(o1, o2){
...
}
etc...
}
I want to write function which returns certain comparator depending on input data. It should figure out comparator from string inputs, something like this:
function chooseComparator(field, order){
return "comparators."+order+"_"+field+"_comparator";
}
So is it possible to pass only function name string to sort() method or I'll need to pass reference to correct comparator somehow?
use the subscript notation for indexing the javascript object (obj.prop is the same as obj["prop"], but the latter way you can create property names dynamically):
function chooseComparator(field, order){
return comparators[order+"_"+field+"_comparator"];
}
and yes, you have to pass a function object to the sort() function, just a name is not enough
Actually you can create a closure instead of writing dozens of functions. Assuming asc_firstname_comparator means "sort by x.firstname",
function compareByProperty(field, order) {
return function (o1, o2) {
var retval;
if (o1[field] > o2[field])
retval = 1;
else if (o1[field] < o2[field])
retval = -1;
else
retval = 0;
if (order === "desc")
retval = -retval;
return retval;
}
}
...
myArray.sort(compareByProperty("firstname", "asc"));
I'd do something like this.
var comparators = {
asc_firstname_comparator : function(o1, o2){ ... }
desc_firstname_comparator : function(o1, o2){ ... }
};
Array.prototype.customSort(comparatorName) {
this.sort(comparators[comparatorName]);
}
var myArray = [ ... ]; // define array
myArray.customSort("asc_firstname_comparator");
Related
For example, can I create a method which can return me an expression that can be evaluated by if?
function getCondition(variable, value, operator)//not sure what params to pass
{
var condition = false; //initialized to false
//generate condition based on parameter passed
return condition;
}
and then use it directly
if ( getCondition( a, 5, "<" ) ){ console.log("correct") }
Yes.
In your example, which probably is not your actual use-case, you'd simply have to map your operator:
function getCondition( x, y, op ) {
switch ( op ) {
case '<':
return x < y
case '>':
return x > y
default:
throw new Error( 'operator not understood' )
}
}
if ( getCondition( 1, 5, '<' ) ) {
...
}
You might see this pattern commonly in something like a physics simulation, where you need operators that do not exist natively, such as dot or cross products. I've never seen a use-case where you'd want to pass that operator explicitly to a function though, rather, just create the functions you need for each operator.
You could pass the expression as a parameter
var a = 3.5;
function getCondition(bool) {
var condition = false;
return bool || condition
}
if (getCondition(a < 5)) {
console.log("correct")
}
You probably want to evaluate arguments when you apply the condition, not when you define it. Here's one possibility:
var operator = {};
operator.greaterThan = function(val) {
return function(x) {
return x > val;
}
};
operator.lessThan = function(val) {
return function(x) {
return x < val;
}
};
isLessThan5 = operator.lessThan(5);
a = 4;
if(isLessThan5(a)) console.log('ok'); else console.log('not ok');
b = 10;
if(isLessThan5(b)) console.log('ok'); else console.log('not ok');
For complex conditions you can also add boolean operators:
operator.and = function() {
var fns = [].slice.call(arguments);
return function(x) {
return fns.every(f => f(x));
}
};
operator.or = function() {
var fns = [].slice.call(arguments);
return function(x) {
return fns.some(f => f(x));
}
};
isBetween5and10 = operator.and(
operator.greaterThan(5),
operator.lessThan(10));
if(isBetween5and10(8)) console.log('ok')
if(isBetween5and10(15)) console.log('ok')
Yes, but you have to define in the function what the operator means. So your function needs to contain some code along the lines of:
if (operator === '>') {
condition = (value1 > value2);
}
You could also use string concatenation and eval, but I wouldn't recommend it:
condition = eval(value1 + operator + value2);
Yes, you can use the return value of a method if it can be evaluated to either true or false.
The sample code you provided should work as you expect it.
The return value of the method can also be evaluated from an int or a string to a boolean value. Read more about that here: JS Type Coercion
It is possible to pass a function or expression to an if. Like you're saying yourself, an if accepts an expression... that evaluates to either true or false. So you can create any function or method that returns a boolean value (not entirely true in PHP and other weak typed languages).
Clearly, since PHP isn't strongly typed, no function guarantees that it returns a boolean value, so you need to implement this properly yourself, as doing this makes you prone to getting errors.
I've got a object like this:
{"status":200,
"success":true,
"result": [ {"Description":"", "Year":"", "Price/STK":"", "Main Cat":"Fruits"} ]
}
I have distinct lists I need to use, and the Price key can be: Price/STK, Price/Box, Price/Btl or Price.
I know I can get the value using, for example, data.result['Price/STK'], but I don't want to check every key, I'd like to search for the price and just use.
How would I determine if a word ('Price*', for example) is part of a key and get that value?
There's no built in way to do this, you have to iterate and check each key.
You could just create a convenient function :
function matchKey(objectToSearch, keyToFind) {
for (var k in objectToSearch) {
if ( k.toLowerCase().indexOf(keyToFind.toLowerCase()) !== -1)
return objectToSearch[k];
}
return null;
}
matchKey({year : 2015, "Price/STK" : "value"}, "price"); // returns "value"
FIDDLE
You could solve this problem easily using lodash (or underscore)
_.findKey(obj, function(key) { return _.startsWith(key, 'Price')})
This finds the first key that starts with price.
You can get the property names of an object using Object.keys, and then use indexOf to search for a value, but it does an exact match and doesn't take a regular expression as an argument.
So you have to loop over all the property names until you find the one you want. There are built–in iterators to help:
var obj = {"status":200,
"success":true,
"result": [ {"Description":"desc",
"Year":"yr",
"Price/STK":"price/stk",
"Main Cat":"Fruits"}
]
};
function getValueLike(obj, prop){
var re = new RegExp('^' + prop);
var value;
Object.keys(obj).some(function(prop) {
if (re.test(prop)) {
value = obj[prop];
return true;
}
});
return value;
}
document.write(getValueLike(obj.result[0], 'Price')); // price/stk
A version that uses indexOf on the property name might be faster and is a little less code:
function getValueLike(obj, prop){
var value;
Object.keys(obj).some(function(key) {
if (key.indexOf(prop) == 0) {
value = obj[key];
return true;
}
});
return value;
}
which can be reduced to:
function getValueLike(obj, prop, value){
Object.keys(obj).some(function(key) {return key.indexOf(prop) == 0 && ((value = obj[key]) || true)});
return value;
}
which also allows a default value to be passed to value, but it's a little too obfuscated for me.
Using an arrow function:
function getValueLike(obj, prop, value){
Object.keys(obj).some(key => key.indexOf(prop) == 0 && ((value = obj[key]) || true));
return value;
}
Filter the set of keys on the result array's object for "Price", and then return the value associated with that. I made a function for it as an example.
function selectPrice(obj){
return obj.result[0][
Object.keys(obj.result[0]).filter(function(el){
return el.indexOf("Price") > -1
})[0]
]
}
var data = {"status":200,
"success":true,
"result": [ {"Description":"", "Year":"", "Price/STK":"6", "Main Cat":"Fruits"} ]
};
document.write(selectPrice(data));
I am trying to sort through arrays of n-dimensional objects based on a certain index. For example if I have an array:
var array1 = [['c',24,"John"],['a',-13,"Cindy"],['b',98,"Alex"]];
I want to be able to sort by any dimension. Does anything exist in javascript that would give me outputs that look like the following?
array1.sort(sortFunction(0));
- [['a',-13,"Cindy"],['b',98,"Alex"],['c',24,"John"]]
array1.sort(sortFunction(1));
- [['a',-13,"Cindy"],['c',24,"John"],['b',98,"Alex"]]
array1.sort(sortFunction(2));
- [['b',98,"Alex"],['a',-13,"Cindy"],['c',24,"John"]]
Thanks!
To clarify: I am aware you must write a sort function, but can you write one that takes in an argument? Here would be an example:
function sortFunction(dim){
if (a[dim] === b[dim]) {
return 0;
}
else {
return (a[dim] < b[dim]) ? -1 : 1;
}
}
You can write a function generator that take the index of the parameters in your array :
var array = [['a',24,"John"],['a',-13,"Cindy"],['b',98,"Alex"]];
function sorting(index) {
return function(a, b) {
return a[index] < b[index]?-1:(a[index] > b[index])?1:0;
}
}
var res1 = array.sort(sorting(0));
var res2 = array.sort(sorting(1));
var res3 = array.sort(sorting(2));
Note: this is the basic approach of how the sort callback function works. For a more elegant solution see Paul Boute’s answer. His solution is especially useful when the function needs to be applied in more than one situation.
Your sort function needs to compare two properties of the elements (or the two elements) themselves. It needs to return a negative number if they are in the correct order, 0 if they are equivalent and a positive number if they are in the wrong order.
A JavaScript example of such a function:
function sortFunction(elementA,elementB,index){
if(elementA[index]<elementB[index]){
return -1;
}
else if(elementA[index]>elementB[index]){
return 1;
}
else{
return 0;
}
}
Invokable as such:
array1.sort(function(a,b){
return sortFunction(a,b,1);
});
Or the shorter all-in-one version:
array1.sort(function(a,b){
var index=1;
if(a[index]<b[index]){
return -1;
}
else if(a[index]>b[index]){
return 1;
}
else{
return 0;
}
});
You can also use a library like underscore.js. Then, simply
// which index to sort on
var s = 1;
// sort it - it will work for objects too
_.sortBy(array1, function(a){ return a[s];});
// or simply use "property name" to sort on
_.sortBy( array1, s);
How can test over an object like the array.every() method? Trying to detect if all the terms in a query object are blank before sending it on. Obviously I could just write a little routine in a for loop, but I'm expecting there's a more succinct way of accomplishing this.
// array - works
var queryArr = [ "", "" ];
if(!queryArr.every(function(el, i, arr) { return el == "" } )) {
alert("nothing to search");
}
// object - "undefined is not a function"
var queryObj = { term1: "", term2: "" };
if(!queryObj.every(function(el, i, arr) { return el == "" } )) {
alert("nothing to search");
}
Map can be used call a function on each object in an array.
http://api.jquery.com/jquery.map/
You could implement your own every method this way:
Object.prototype.every=function(evalFunction){
var self=this,
property;
if(typeof evalFunction!=='function')
return evalFunction;
for(property in self){
if(self.hasOwnProperty(property) && !evalFunction(self[property], property, self)){
return false;
}
}
return true;
}
Then you could use the new every method on objects the same way you do with arrays But if you only want to test this once on your code I sugest to use for in loop or robisrob's map solution
I have an object in javaScript:
var stuffObject = {
stuffArray1 : [object1, object2, object3],
stuffArray2 : [object4, object5, object6]
}
object1 to 6 look like this:
object1 = {
dataStuff : {
stuffId: "foobar"
}
}
My question: given the key "foobar", how do I retrieve object1 from the stuffObject using jQuery? The key "stuffId" always has a unique value.
You won't get around iterating over the set to find the object you are looking for. jQuery can't really help with that. Its purpose is DOM manipulation. If you want functionality to deal with objects, sets, lists, etc., check out lodash.
I wrote a function to deal with the problem. I hope it's understandable.
var stuffObject = {
stuffArray1 : [{dataStuff: {stuffId: 'foobar'}}, {dataStuff: {stuffId: 'foo'}}, {}],
stuffArray2 : [{}, {dataStuff: {stuffId: 'bar'}}, {}]
}
function getObjByStuffId(stuffObject, stuffId) {
var key, arr, i, obj;
// Iterate over all the arrays in the object
for(key in stuffObject) {
if(stuffObject.hasOwnProperty(key)) {
arr = stuffObject[key];
// Iterate over all the values in the array
for(i = 0; i < arr.length; i++) {
obj = arr[i];
// And if it has the value we are looking for
if(typeof obj.dataStuff === 'object'
&& obj.dataStuff.stuffId === stuffId) {
// Stop searching and return the object.
return obj;
}
}
}
}
}
console.log('foobar?', getObjByStuffId(stuffObject, 'foobar') );
console.log('foo?', getObjByStuffId(stuffObject, 'foo') );
console.log('bar?', getObjByStuffId(stuffObject, 'bar') );
Thanks for the help guys, using the input of other people I have solved it myself:
getStuffById: function(id){
for (stuffArray in stuffObject) {
for (stuff in stuffObject[stuffArray]) {
if (stuffObject[stuffArray][stuff].dataStuff.stuffId == id) {
return stuffObject[stuffArray][stuff];
}
}
}
return null;
}
This also works better than the (now deleted) answer that uses .grep(), as this function terminates as soon as it finds the correct object.