I have a dgrid table comprising 13 columns.
The sort in decreasing order works as expected: Highest values, followed by lowest values, followed by entries that have no values.
However, when I sort in ascending order, there are fields with no values, then fields with 0, then fields with no values again and finally fields with values in ascending order.
I have looked into source code, but I am unable to figure out what is causing this.
Is this a bug with dgrid sorting?
What could be the workaround for this?
Provided that your column is read-only, you could add a field in the data source (see displayValue in the code below) reflecting your values, but where strings are replaced with a negative number.
Then, instead of the real field, put only this field in the grid. And display the real value in the get function of the column.
I've used the same workaround to apply a case-insensitive sort in a column populated with proper names.
require(["dgrid/Grid", "dojo/domReady!"], function (Grid) {
var data = [];
for(var i = 0; i < 3; i++) for(j = 1; j <= 2; j++) data.push({value: i});
for(i = 1; i < 3; i++) data.push({value: ""});
data.forEach(function(item) {
item.displayValue = typeof item.value == "string" ? -1 : item.value;
});
var grid = new Grid({
columns: {
displayValue: {
label: "Value",
get: function(item) { return item.value; }
}
}
}, 'grid');
grid.renderArray(data);
grid.set("sort", "displayValue");
});
<script>
dojoConfig = {
async: true,
packages: [
{
name: 'dgrid',
location: 'https://cdn.rawgit.com/SitePen/dgrid/v1.1.0'
}
]
}
</script>
<script src="https://ajax.googleapis.com/ajax/libs/dojo/1.11.2/dojo/dojo.js"></script>
<link rel="stylesheet" href="https://cdn.rawgit.com/SitePen/dgrid/v1.1.0/css/dgrid.css"/>
<style>
.dgrid-row-table {
height: 24px;
}
</style>
<body>
<div id="grid" style="width:200px;height:210px">
</div>
</body>
= Better answer as my last post, after your comments =
AFAIK, dgrid (and also dstore) doesn't allow to use a custom sort function.
The pattern below overcomes this limitation. It uses an OnDemandGrid populated with a dstore.
For instance, the field1 column of this grid contains values ranging from "g1" to "g20". They are not sorted aphanumerically (default sort) but numerically (custom sort based on the numbers after "g") => "g1" < "g2" < "g3" < ... < "g10" < "g11"...
This custom sort is performed by the comparegXX callback function:
var comparegXX = function(a, b) {
// 'compare' is a generic comparison function for values of the same type
try {
return compare(parseInt(a.value.substr(1), 10), parseInt(b.value.substr(1), 10));
}
catch(ex) { return compareMixedTypes(a, b); }
}
... and comparegXX is assigned to field1 in its column definition (sort attribute):
field1: {
sort: comparegXX
},
It' the same for field2 (mixed types sort - strings and numbers - performed by compareMixedTypes).
If you want to assign another custom sort to a field, write your custom comparison callback function and add it in the column definition of the field: fieldX: {sort: myCompareFunction}.
Note that the store shouldn't contain any field named _newPos. This field is created and used by the doSort function. It contains the new relative positions of the data rows after applying the custom sort - the new sort of the grid is based on this field.
var compare = function(a, b) { return a > b ? 1 : a < b ? -1 : 0; }
// comparison functions for custom sorts
// use a.value and b.value in these functions, not directly a and b
var comparegXX = function(a, b) {
try {
return compare(parseInt(a.value.substr(1), 10), parseInt(b.value.substr(1), 10));
}
catch(ex) { return compareMixedTypes(a, b); }
}
var compareMixedTypes = function(a, b) {
var aType = typeof a.value;
return aType == typeof b.value ? compare(a.value, b.value) :
aType == "string" ? -1 : 1;
}
require(["dstore/Memory", "dgrid/OnDemandGrid", "dojo/domReady!"], function (Memory, OnDemandGrid) {
// populate the store (random values in field2)
var tbl = [];
for(var i = 1; i < 21; i++) {
var item = {id: i};
item.field1 = "g" + i;
item.field2 = (i == 1 || Math.random() < 0.2) ? "" : Math.floor(Math.random() * 10);
tbl.push(item);
}
var store = new Memory( {data: tbl });
var grid = new OnDemandGrid({
collection: store,
columns:
{
id: {},
field1: {
sort: comparegXX
},
field2: {
sort: compareMixedTypes
}
},
}, 'grid');
var lastField = null;
var descending = null;
grid.doSort = function(e) {
// custom sort of the grid, replaces the default sort
var field = e.sort[0].property;
if(lastField == field) descending = !descending;
else {
lastField = field;
if(descending == null) descending = e.sort[0].descending;
}
var sortFunc = grid.column(field).sort;
if(sortFunc) {
// calculate the positions of the rows based on the custom compare function,
// they are stored in the _newPos field, and then the grid is sorted on it
var tmp = [], tmp2 = {};
store.forEach(function(item, i) { tmp.push({value: item[field], pos: i}); });
tmp.sort(sortFunc);
tmp.forEach(function(item, i) { tmp2[item.pos] = i; });
store.forEach(function(item, i) { item._newPos = tmp2[i]; });
grid.set("sort", "_newPos", descending);
}
else grid.set("sort", field, descending);
grid.updateSortArrow([{property: field, descending: descending}]);
}
grid.on("dgrid-sort", function(e) {
grid.doSort(e);
e.preventDefault();
});
// initial sort of the grid, use this instead of grid.set("sort"...)
grid.doSort({sort: [{property: "field1", descending: false}]});
grid.startup();
});
<script>
dojoConfig = {
async: true,
packages: [
{
name: 'dgrid',
location: 'https://cdn.rawgit.com/SitePen/dgrid/v1.1.0'
},
{
name: 'dstore',
location: '//cdn.rawgit.com/SitePen/dstore/v1.1.1'
}
]
}
</script>
<script src="https://ajax.googleapis.com/ajax/libs/dojo/1.11.2/dojo/dojo.js"></script>
<link rel="stylesheet" href="https://cdn.rawgit.com/SitePen/dgrid/v1.1.0/css/dgrid.css" />
<body>
<div id="grid" style="width:300px;height:530px;">
</div>
</body>
Related
I have a bunch of filter criteria stored in an object. The criteria changes from time to time, so I can't have a static filter (ie: price > 5 && price < 19 && ...).
var criteria = {
price: {
min: 5,
max: 19
},
age: {
max: 35
}
};
I then have a loop setup to filter through an array based on the criteria and return the filtered array:
var filtered = [];
var add = true;
for (var i=0; i < data.length; i++ ){
add = true;
var item = data[i];
for (var main in criteria){
for (var type in criteria[main] ){
if ( type === 'min') {
if ( !(item[main] > criteria[main][type]) ) {
add = false;
break;
}
} else if ( type === 'max') {
if ( !(item[main] < criteria[main][type]) ) {
add = false;
break;
}
}
}
}
if (add) {
filtered.push(item);
}
}
Is there a more efficient way to setup the filter conditionals ahead of time (ie: item.price > 5 && item.price < 19 && item.age < 35) and then filter the array? As opposed to what I'm currently doing and referencing the object during each array loop - which is inefficient with all the conditionals and sub-loops.
See my jsbin - http://jsbin.com/celin/2/edit .
i would use Array.prototype.filter:
var filtered = data.filter(function (item) {
var main, critObj;
for (main in criteria) {
critObj = criteria[main];
if (critObj.min && critObj.min >= item[main]) {
return false;
}
if (critObj.max && critObj.max <= item[main]) {
return false;
}
}
return true;
});
return falseif it should not be included in your filtered list. inside the for-loop, the function just checks if the criteria has a min, and if if this is bigger than the same property in the array item. if so, it just returns false for this element (the same of course for the max-property).
if both fit, the function returns true, and i will be included in your filtered list!
edit: now with fixed bin
I've been working on the Ramda library, and using it to do this is fairly straightforward:
var test = R.allPredicates(R.reduce(function(tests, key) {
var field = criteria[key];
if ('min' in field) {tests.push(R.pipe(R.prop(key), R.gt(R.__, field.min)));}
if ('max' in field) {tests.push(R.pipe(R.prop(key), R.lt(R.__, field.max)));}
return tests;
}, [], R.keys(criteria)));
console.log( 'filtered array is: ', data.filter(test) );
(also available in this JSBin.)
To do this without a library, I converted the code above to into a library-less version, and it's a bit more complicated, but still readable:
var test = (function(criteria) {
var tests = Object.keys(criteria).reduce(function(tests, key) {
var field = criteria[key];
if ('min' in field) {tests.push(function(item) {
return item[key] > field.min;
});}
if ('max' in field) {tests.push(function(item) {
return item[key] < field.max;
});}
return tests;
}, []);
return function(item) {
return tests.every(function(test) {return test(item);});
};
}(criteria));
console.log( 'filtered array is: ', data.filter(test) );
(JSBin)
In either version, the criteria is parsed once to create a set of predicate functions. Those functions are combined into a single predicate which is passed as a filter.
$.each(constructions, function(i,v) {
if ($.inArray(v.name, map[ii].buildings) == -1) {//stuff}
};
Where constructions is an array of objects, each with a unique name. map[ii].buildings is an array containing some of these objects. I want to iterate each object in constructions, checking if its name parameter appears in the objects of map[ii].buildings.
The above code works if the each element in the map[ii].buildings array is just the text string of the object name, but not if the element is the entire object.. close, but no dice >.<
Try using $.grep() instead of $.inArray(); you can specify a function to do the filtering for you.
Instead of checking for -1, you check whether the array that $.grep() returns has length == 0
Simple example: (would be easier if you posted the code / example of what "constructions" objects look like)
var constructions = [{
Name: "Mess hall",
SqFt: 5000
}, {
Name: "Infirmary",
SqFt: 2000
}, {
Name: "Bungalow",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
var buildings = [{
Name: "Infirmary",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
// found buildings will be list of items in "constructions" that is not in "buildings"
var foundBuildings = $.grep(constructions, function (constructionsItem) {
return $.grep(buildings, function (buildingsItem) {
return buildingsItem.Name === constructionsItem.Name
}).length == 0; // == 0 means "not in", and > 0 means "in"
});
// this just renders the results all pretty for ya
$.each(foundBuildings, function (idx, item) {
$("#output").append("<div>" + item.Name + "</div>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='output'></div>
Example jsFiddle: http://jsfiddle.net/eLeuy9eg/3/
The non-jQuery way of doing this would be to use filter. Something like this:
// pass in an array and the key for which you want values
// it returns an array of those values
function getValues(arr, key) {
return arr.map(function (el) { return el[key]; });
}
function notFoundIn(arr, arr2) {
// grab the names of the buildings
var buildings = getValues(arr2, 'name');
// grab the names from the construction objects and filter
// those that are not in the building array
return getValues(arr, 'name').filter(function (el) {
return buildings.indexOf(el) === -1;
});
}
notFoundIn(constructions, buildings); // eg [ "one", "three" ]
DEMO
You could even add a new method to the array prototype. With this one you can use either simple arrays, or arrays of objects if you pass in a key. Note in this example I've replaced map and filter with loops that perform the same functions, but faster (see comments):
function getValues(arr, key) {
var out = [];
for (var i = 0, l = arr.length; i < l; i++) {
out.push(arr[i][key]);
}
return out;
}
if (!Array.prototype.notFoundIn) {
Array.prototype.notFoundIn = function (inThisArray, key) {
var thisArr = key ? getValues(this, key) : this;
var arrIn = key ? getValues(inThisArray, key) : inThisArray;
var out = [];
for (var i = 0, l = thisArr.length; i < l; i++) {
if (arrIn.indexOf(thisArr[i]) === -1) {
out.push(thisArr[i]);
}
}
return out;
}
}
constructions.notFoundIn(buildings, 'name');
[1, 2, 3].notFoundIn([2]); // [1, 3]
DEMO
Well, I have an array objects with random values,
Ex.
var arr = [
{ id:1001, date:"20-02-2014", Name: 'demo1' },
{ id:1004, date:"13-02-2014", Name: 'demo0' },
{ id:1000, date:"10-02-2014", Name: 'demo14' },
{ id:1004, date:"16-02-2014", Name: 'demo10' },
{ id:1006, date:"22-02-2014", Name: 'demo111' },
{ id:1003, date:"28-02-2014", Name: 'demo16' },
{ id:1000, date:"28-01-2014", Name: 'demo12' },
{ id:1004, date:"28-01-2014", Name: 'demo01' },
{ id:1000, date:"08-01-2014", Name: 'demo41' },
{ id:1006, date:"08-01-2014", Name: 'demo91' }
]
I wanted to sort this array firstly by key id & then by key date as,
Output:
sorted_arr = [
{"id":1000,"date":"08-01-2014","Name":"demo41"}, //group1
{"id":1000,"date":"28-01-2014","Name":"demo12"}, //group1
{"id":1000,"date":"10-02-2014","Name":"demo14"}, //group1
{"id":1001,"date":"20-02-2014","Name":"demo1"}, //group2
{"id":1003,"date":"28-02-2014","Name":"demo16"}, //group3
{"id":1004,"date":"28-01-2014","Name":"demo01"}, //group4
{"id":1004,"date":"13-02-2014","Name":"demo0"}, //group4
{"id":1004,"date":"16-02-2014","Name":"demo10"}, //group4
{"id":1006,"date":"08-01-2014","Name":"demo91"} //group5
{"id":1006,"date":"22-02-2014","Name":"demo111"} //group5
]
I tried few generic code to sort,
// generic comparison function
cmp = function(x, y){
return x > y ? 1 : x < y ? -1 : 0;
};
arr.sort(function(a, b){
return cmp(
[cmp(a.id, b.id), cmp(a.date, b.date)],
[cmp(b.id, a.id), cmp(b.date, a.date)]
);
});
I referred few examples SO Example but not getting expected output. Please suggest me best way to get this.
No need to create Date objects, just reorder the date string into a sortable string, example
This example assumes that your dates are in the same format DD-MM-YYYY and creates YYYYMMDD for the date sort.
Javascript
var arr = [
{ id:1001, date:"20-02-2014", Name: 'demo1' },
{ id:1004, date:"13-02-2014", Name: 'demo0' },
{ id:1000, date:"10-02-2014", Name: 'demo14' },
{ id:1004, date:"16-02-2014", Name: 'demo10' },
{ id:1006, date:"22-02-2014", Name: 'demo111' },
{ id:1003, date:"28-02-2014", Name: 'demo16' },
{ id:1000, date:"28-01-2014", Name: 'demo12' },
{ id:1004, date:"28-01-2014", Name: 'demo01' },
{ id:1000, date:"08-01-2014", Name: 'demo41' },
{ id:1006, date:"08-01-2014", Name: 'demo91' }
];
var sorted = arr.sort(function (a, b) {
return a.id - b.id || a.date.split('-').reverse().join('') - b.date.split('-').reverse().join('');
});
sorted.forEach(function (element) {
console.log(JSON.stringify(element));
});
Output
{"id":1000,"date":"08-01-2014","Name":"demo41"}
{"id":1000,"date":"28-01-2014","Name":"demo12"}
{"id":1000,"date":"10-02-2014","Name":"demo14"}
{"id":1001,"date":"20-02-2014","Name":"demo1"}
{"id":1003,"date":"28-02-2014","Name":"demo16"}
{"id":1004,"date":"28-01-2014","Name":"demo01"}
{"id":1004,"date":"13-02-2014","Name":"demo0"}
{"id":1004,"date":"16-02-2014","Name":"demo10"}
{"id":1006,"date":"08-01-2014","Name":"demo91"}
{"id":1006,"date":"22-02-2014","Name":"demo111"}
On jsFiddle
If there is any concern over mixing date formats, as discussed with #xdazz, then you can improve on this by checking the padding yourself. The following creates the format 'YYYYYYMMDD' when sorting by the date. The extra year padding is not necessary in this example as I am taking the numeric difference of the values, but if you choose to compare the strings then it is important.
function pad(s, n) {
var v = '',
i;
for(i = 0; i < n - s.length; i += 1) {
v += '0';
}
return v + s;
}
var sorted = arr.sort(function (a, b) {
var idDiff = a.id - b.id;
if (idDiff) {
return idDiff;
}
var ordA = a.date.split('-').reverse(),
ordB = b.date.split('-').reverse();
ordA[0] = pad(ordA[0], 6);
ordA[1] = pad(ordA[1], 2);
ordA[2] = pad(ordA[2], 2);
ordA = ordA.join('');
ordB[0] = pad(ordB[0], 6);
ordB[1] = pad(ordB[1], 2);
ordB[2] = pad(ordB[2], 2);
ordB = ordB.join('');
return ordA - ordB;
});
On jsFiddle
If you really want to use Date objects the I would suggest the following.
var sorted = arr.sort(function (a, b) {
var idDiff = a.id - b.id;
if (idDiff) {
return idDiff;
}
var ordA = a.date.split('-').reverse(),
ordB = b.date.split('-').reverse();
ordA[1] -= 1;
ordB[1] -= 1;
return new Date(Date.UTC.apply(undefined, ordA)).valueOf() - new Date(Date.UTC.apply(undefined, ordB)).valueOf();
});
sorted.forEach(function (element) {
console.log(JSON.stringify(element));
});
On jsFiddle
Note: These examples do not handle dates with negative years, again you would need to make further modifications.
First compare with id, then compare with date if id equal. But because your date is in invalid date format, extra work has to be done for letting it be recognized by Date.
sorted_arr = arr.sort(function(a, b) {
return a.id - b.id || new Date(a.date.split('-').reverse().join('-')) - new Date(b.date.split('-').reverse().join('-'));
});
Edit:
If you are guaranteed to have zeros in front of the 1-digit months and dates, then you could even not to parse to date:
sorted_arr = arr.sort(function(a, b) {
return a.id - b.id || a.date.split('-').reverse().join('') - b.date.split('-').reverse().join('');
});
It's better to have a date operate lib momentjs to help.
You could check the code on jsBin
function compare(a, b){
var idDiff = a.id - b.id;
var adate = moment(a.date, "DD-MM-YYYY");
var bdate = moment(b.date, "DD-MM-YYYY");
var dateDiff = adate.diff(bdate);
return idDiff || dateDiff;
}
var sortedArr = arr.sort(compare);
console.log(sortedArr);
You can sort array by two properties with Alasql library:
var res = alasql('SELECT *, mid(date,7,4)+mid(date,4,2)+mid(date,1,2) AS ndate \
FROM ? ORDER BY id, ndate',[arr]);
Try this example at jsFiddle.
Here "mid(date,7,4)+mid(date,4,2)+mid(date,1,2)" was used to convert date from '28-11-2014' to the sort key like '20141128'.
Came up with this using underscore.js & it's chain and sortBy:
var sorted_array = _(arr).chain().sortBy(function(o) {
return o.date.split('-').reverse().join();
}).sortBy(function(o) {
return o.id;
}).value();
Sorting on the date first, and then id will give you the list sorted as expected.
jsfiddle
I have two arrays of objects like below:
items = [{"id":"5","tobuy":"1","name":"pop"},
{"id":"6","tobuy":"1","name":"fish"},
{"id":"7","tobuy":"0","name":"soda"}]
pkgs = [{"item_id":"5","store":"Market","aisle":"3"},
{"item_id":"6","store":"Market","aisle":"2"},
{"item_id":"6","store":"Dept","aisle":"8"},
{"item_id":"7","store":"Market","aisle":"4"}]
I'm trying to sort the items array, but I want to leverage the data in the pkgs array.
The "item_id" field in the pkgs array corresponds to the "id" field in the items array.
For example, I want to sort:
first by "tobuy" in descending order
then by "store"
then by "aisle"
then by "name"
While item_id and id correspond between the two arrays, there is not a 1 to 1 relationship. There could be 0 or more pkgs that correspond to any given item.
(If I had a database, I would just join the tables, but in JavaScript I just have the two related arrays).
I'm not sure how to build the comparator function and pass in the second array.
Thanks for any help.
Perhaps something like this?
items = items.map(function (item, index) {
return {
item: item,
pkg: pkgs[index] //I assumed associated pkgs were at the same index
};
}).sort(function (a, b) {
var pkgA = a.pkg, pkgB = b.pkg, r;
r = +b.item.tobuy - +a.item.tobuy;
if (r !== 0) return r;
r = pkgA.store < pkgB.store? -1 : (pkgA.store === pkgB.store? 0 : 1);
if (r !== 0) return r;
r = +pkgA.aisle - +pkgB.aisle;
if (r !== 0) return r;
return pkgA.name < pkgB.name? -1 : (pkgA.name === pkgB.name? 0 : 1);
}).map(function (item) {
return item.item;
});
Instead of merging the data, you could also create a lookup map that allows to quickly retrieve the associated package directly from the sort function.
E.g.
var pkgsMap = pkgs.reduce(function (res, pkg) {
res[pkg.item_id] = pkg;
return res;
}, {});
Then in the sort function you can do:
var pkgA = pkgsMap[a.id], pkgB = pkgsMap[b.id];
EDIT:
There is actually another field in the pkgs array called "ppu" which
is the price per unit. The lowest ppu is the one that would be used.
You can just build your package map using the following and then use the map in the sort function to retrieve the associated package like described above and implement the sort algorithm.
var pkgsMap = pkgs.sort(function (a, b) {
//not sure what ppu is so I sort it as a string
return a.ppu < b.ppu? -1 : Number(a.ppu > b.ppu);
}).reduce(function (res, pkg) {
if (!(pkg.item_id in res)) res[pkg.item_id] = pkg;
return res;
}, {});
Make a function that generates a comparator, this looks unwieldy but means you can generate any sort order desired
function generateComparator(dict, index, order) {
return function (a, b) {
var i, key, direction,
ai = a[index], av,
bi = b[index], bv;
for (i = 0; i < order.length; ++i) {
key = order[i].key;
direction = +!!order[i].reverse || -1;
if (dict[ai].hasOwnProperty(key)) // if in dict, lookup
av = dict[ai][key];
else // else look up in item
av = a[key];
if (dict[bi].hasOwnProperty(key))
bv = dict[ai][key];
else
bv = b[key];
// console.log(i, key, av, bv, direction); // debug
if (av === bv)
continue;
if (av < bv)
return direction;
return -direction;
}
return 0;
};
}
Convert your Arrays into a dictionary
var dict = (function (arr, index) {
var o = {}, i;
for (i = 0; i < arr.length; ++i) {
o[arr[i][index]] = arr[i];
}
return o;
}(pkgs, 'item_id'));
Define your sort choices
var order = [
{key: 'tobuy', reverse: 1},
{key: 'store'},
{key: 'aisle'},
{key: 'name'}
];
Generate comparator with dictionary
var comparator = generateComparator(dict, 'id', order);
Then sort your first Array
items.sort(comparator);
/* [
{"id": "6", "tobuy": "1", "name": "fish"},
{"id": "5", "tobuy": "1", "name": "pop"},
{"id": "7", "tobuy": "0", "name": "soda"}
] */
Let's consider how you would do this in SQL:
SELECT * FROM items INNER JOIN pkgs ON items.id = pkgs.item_id
ORDER BY tobuy DESC, store, aisle, name
The following answer demonstrates how to implement an inner join and an equijoin in JavaScript:
function equijoin(primary, foreign, primaryKey, foreignKey, select) {
var m = primary.length, n = foreign.length, index = [], c = [];
for (var i = 0; i < m; i++) { // loop through m items
var row = primary[i];
index[row[primaryKey]] = row; // create an index for primary table
}
for (var j = 0; j < n; j++) { // loop through n items
var y = foreign[j];
var x = index[y[foreignKey]]; // get corresponding row from primary
c.push(select(x, y)); // select only the columns you need
}
return c;
}
Now you can use equijoin to join items and pkgs as follows:
equijoin(items, pkgs, "id", "item_id", function (item, pkg) {
return {
id: +item.id,
tobuy: +item.tobuy,
store: pkg.store,
aisle: +pkg.aisle,
name: item.name
};
});
Note that I'm coercing item.id, item.tobuy and pkg.aisle to numbers by applying the unary + operator to them.
Now that we joined the two tables we need to sort them. To sort the table we use the built-in array sort method:
.sort(function (a, b) {
// ORDER BY tobuy DESC
var aTobuy = a.tobuy, bTobuy = b.tobuy;
if (aTobuy < bTobuy) return 1;
if (aTobuy > bTobuy) return -1;
// ORDER BY store
var aStore = a.store, bStore = b.store;
if (aStore < bStore) return -1;
if (aStore > bStore) return 1;
// ORDER BY aisle
var aAisle = a.aisle, bAisle = b.aisle;
if (aAisle < bAisle) return -1;
if (aAisle > bAisle) return 1;
// ORDER BY name
var aName = a.name, bName = b.name;
if (aName < bName) return -1;
if (aName > bName) return 1;
// keep them unchanged
return a.id - b.id;
});
The sort method is unstable (i.e. it might not preserve the ordering of items with equal sort value in the input list). Hence to workaround this limitation we return a.id - b.id as the last statement.
Also notice that we're comparing all the values (whether strings or numbers) using < and >. Strings are compared lexicographically while numbers are compared numerically.
Put together the code is as follows:
var table = equijoin(items, pkgs, "id", "item_id", function (item, pkg) {
return {
id: +item.id,
tobuy: +item.tobuy,
store: pkg.store,
aisle: +pkg.aisle,
name: item.name
};
}).sort(function (a, b) {
var aTobuy = a.tobuy, bTobuy = b.tobuy;
if (aTobuy < bTobuy) return 1;
if (aTobuy > bTobuy) return -1;
var aStore = a.store, bStore = b.store;
if (aStore < bStore) return -1;
if (aStore > bStore) return 1;
var aAisle = a.aisle, bAisle = b.aisle;
if (aAisle < bAisle) return -1;
if (aAisle > bAisle) return 1;
var aName = a.name, bName = b.name;
if (aName < bName) return -1;
if (aName > bName) return 1;
return a.id - b.id;
});
Not as concise as SQL is it? Anyway, see the demo for yourself: http://jsfiddle.net/7ZG96/
Edit: If you want only want the id, tobuy and name columns then you can extract it from the sorted table using map as follows:
table.map(function (item) {
return {
id: item.id,
tobuy: item.tobuy,
name: item.name
};
});
This corresponds to the following SQL query:
SELECT id, tobuy, name FROM (SELECT * FROM items INNER JOIN pkgs
ON items.id = pkgs.item_id ORDER BY tobuy DESC, store, aisle, name)
See the updated demo: http://jsfiddle.net/7ZG96/1/
I have an array of objects in a known format, it could look like this:
var items = [{id : 1,
desc : "Funny things",
tags : ["Snippet","Funny"],
title : "Awsome"},
{id : 2,
desc : "Hello World",
tags : ["Fun","Funny"],
title : "What"},
{id : 3,
desc : "True story",
tags : ["Snippet","Cool"],
title : "Is it possible with things?"
}];
I want to create some serach ability in my page that serach for diffrent things inside the items and later display it in some way. Does any one know a plugin that could help me with this?
I was just trying using jQuery grep function and came up with this snippet for my example:
var serach = "things"; // Try to get the tags
var obj = $.grep(items, function(n, i){
// Condition one
if(n.desc.indexOf(serach)>=0)
{
return true;
};
// Condition two
if(n.title.indexOf(serach)>=0)
{
return true;
};
// Condition there
var foundTag = false;
for(var i = 0; i<n.tags.length;i++){
if(n.tags[i].indexOf(serach)>=0)
{
foundTag = true;
return true;
};
}
if(foundTag){return true};
return false;
});
http://jsfiddle.net/Az2rA/1/
It's pretty straight forward and works. However it dosn't solve things like priority diffrent properties. How could add a priority to the function. For example, if the serach expression is found in the title it should apper higher in the "matched" array.
So if anyone have any good input or a good plugin I will find it helpful!
You can use jQuery.each creating and array with a weight for each match and then a sort.
Like this:
//var serach = "Fun"; Try the tags
var serach = "things"; // Try to get the tags
var obj = [];
$.each(items, function(i, n) {
// Condition one
if(n.desc.indexOf(serach)>=0)
{
obj.push({ weight: 0, value: n });
};
// Condition two
if(n.title.indexOf(serach)>=0)
{
obj.push({ weight: 10, value: n });
};
// Condition there
var foundTag = false;
for(var i = 0; i<n.tags.length;i++){
if(n.tags[i].indexOf(serach)>=0)
{
foundTag = true;
obj.push({ weight: 5, value: n });
};
}
if(foundTag){
obj.push({ weight: 5, value: n });
};
});
obj.sort( function(a, b) {
return a.weight < b.weight;
});
You can use the map method to wrap each found object into another object along with a priority value. Then you can sort the array on the priority value:
var search = "things";
var obj = $.map(items, function(n, i){
if (n.desc.indexOf(search) != -1) {
return { obj: n, p: 1 };
};
if (n.title.indexOf(search) != -1) {
return { obj: n, p: 2};
};
for (var i = 0; i < n.tags.length; i++) {
if (n.tags[i].indexOf(search) != -1) {
return { obj: n, p: 3}
};
}
return null;
});
obj.sort(function(a, b) {
return a.p == b.p ? 0 : a.p < b.p ? -1 : 1;
});