In Ox.api, add a map option {key: fn(value, object)}, value being object[key], so that the sort value can depend on other keys; Ox.sortBy is updated to use getSortValue (cached sort value of mapped value) instead of getSortValues (precomputed mapped sort value per value, which is no longer unique)
This commit is contained in:
parent
baf29d068b
commit
f5c320187e
1 changed files with 62 additions and 75 deletions
|
@ -9,6 +9,7 @@ Ox.api <f> Turns an array into a list API
|
|||
cache <b|false> If true, cache results
|
||||
enums <o> Enumerables, for example `{size: ['S', 'M', 'L']}`
|
||||
geo <b|false> If true, return combined area with totals
|
||||
map <o> Sort map, for example `{name: function(v, o) { return o.sortName; }}`
|
||||
sort <[o]|[s]> Default sort, for example `['+name', '-age']`
|
||||
sums <[s]> List of keys to be included in totals
|
||||
unique <s|'id'> The name of the unique key
|
||||
|
@ -129,6 +130,17 @@ Ox.api <f> Turns an array into a list API
|
|||
},
|
||||
sort: [{key: 'size', operator: '-'}]
|
||||
});
|
||||
Ox.test.api = Ox.api([
|
||||
{name: 'John Cale', sortName: 'Cale, John'},
|
||||
{name: 'Brian Eno', sortName: 'Eno, Brian'}
|
||||
], {
|
||||
map: {name: function(value, object) { return object.sortName; }},
|
||||
unique: 'name'
|
||||
});
|
||||
Ox.test.apiResults[9] = Ox.test.api({
|
||||
keys: ['name'],
|
||||
sort: [{key: 'name', operator: '+'}]
|
||||
});
|
||||
</script>
|
||||
> Ox.test.apiResults[0].data
|
||||
{items: 3, n: 5}
|
||||
|
@ -148,6 +160,8 @@ Ox.api <f> Turns an array into a list API
|
|||
{positions: {foo: 2, bar: 0}}
|
||||
> Ox.test.apiResults[8].data
|
||||
{items: [{i: 2, size: 'L'}, {i: 1, size: 'M'}]}
|
||||
> Ox.test.apiResults[9].data
|
||||
{items: [{name: 'John Cale'}, {name: 'Brian Eno'}]}
|
||||
@*/
|
||||
Ox.api = function(items, options) {
|
||||
|
||||
|
@ -155,6 +169,7 @@ Ox.api = function(items, options) {
|
|||
cache: options.cache,
|
||||
enums: options.enums ? parseEnums(options.enums) : {},
|
||||
geo: options.geo,
|
||||
map: options.map || {},
|
||||
sort: options.sort || [],
|
||||
sums: options.sums || [],
|
||||
unique: options.unique || 'id'
|
||||
|
@ -162,8 +177,8 @@ Ox.api = function(items, options) {
|
|||
fn = function(options, callback) {
|
||||
var data,
|
||||
keys,
|
||||
result = {data: {}, status: {code: 200, text: 'ok'}},
|
||||
sort = {};
|
||||
map = {},
|
||||
result = {data: {}, status: {code: 200, text: 'ok'}};
|
||||
options = options || {};
|
||||
if (options.query) {
|
||||
// find
|
||||
|
@ -187,9 +202,11 @@ Ox.api = function(items, options) {
|
|||
options.sort.forEach(function(v) {
|
||||
var key = v.key;
|
||||
if (api.enums[key]) {
|
||||
sort[key] = function(value) {
|
||||
map[key] = function(value) {
|
||||
return api.enums[key].indexOf(value.toLowerCase());
|
||||
};
|
||||
} else if (api.map[key]) {
|
||||
map[key] = api.map[key];
|
||||
}/* else if (Ox.isArray(items[0][key])) {
|
||||
sort[key] = function(value) {
|
||||
return value.join(', ');
|
||||
|
@ -197,7 +214,7 @@ Ox.api = function(items, options) {
|
|||
}*/
|
||||
});
|
||||
if (options.keys || options.positions) {
|
||||
result.data.items = sortBy(result.data.items, options.sort, sort, options.query);
|
||||
result.data.items = sortBy(result.data.items, options.sort, map, options.query);
|
||||
}
|
||||
}
|
||||
if (options.positions) {
|
||||
|
@ -566,56 +583,36 @@ Ox.range = function() {
|
|||
|
||||
(function() {
|
||||
|
||||
function getSortValues(array, map) {
|
||||
var mappedArray = map ? array.map(map) : array, length = 0, sort = {};
|
||||
// find numbers, and length of longest number
|
||||
array.forEach(function(value, i) {
|
||||
var mappedValue = mappedArray[i], matches;
|
||||
if (Ox.isString(mappedValue)) {
|
||||
matches = mappedValue.match(/\d+/g);
|
||||
if (matches) {
|
||||
length = Ox.max(matches.map(function(match) {
|
||||
return match.length;
|
||||
}).concat(length));
|
||||
var getSortValue = Ox.cache(function getSortValue(value) {
|
||||
var sortValue = value;
|
||||
function trim(value) {
|
||||
return value.replace(/^\W+/, '');
|
||||
}
|
||||
}
|
||||
});
|
||||
// make lowercase, remove leading non-word characters,
|
||||
// pad numbers and move leading articles to the end
|
||||
array.forEach(function(value, i) {
|
||||
function pad(value) {
|
||||
return value
|
||||
.replace(/^\W+/, '')
|
||||
.replace(/\d+/g, function(match) {
|
||||
return Ox.pad(match, 'left', length, '0');
|
||||
});
|
||||
}
|
||||
var mappedValue = mappedArray[i];
|
||||
if (
|
||||
Ox.isEmpty(mappedValue)
|
||||
|| Ox.isNull(mappedValue)
|
||||
|| Ox.isUndefined(mappedValue)
|
||||
Ox.isEmpty(value)
|
||||
|| Ox.isNull(value)
|
||||
|| Ox.isUndefined(value)
|
||||
) {
|
||||
sort[value] = null;
|
||||
} else if (Ox.isString(mappedValue)) {
|
||||
sort[value] = pad(mappedValue.toLowerCase());
|
||||
sortValue = null;
|
||||
} else if (Ox.isString(value)) {
|
||||
// make lowercase and remove leading non-word characters
|
||||
sortValue = trim(value.toLowerCase());
|
||||
// move leading articles to the end
|
||||
// and remove leading non-word characters
|
||||
Ox.forEach(['a', 'an', 'the'], function(article) {
|
||||
var length;
|
||||
if (new RegExp('^' + article + ' ').test(sort[value])) {
|
||||
length = article.length;
|
||||
sort[value] = pad(
|
||||
sort[value].slice(length + 1) + ', '
|
||||
+ sort[value].slice(0, length)
|
||||
);
|
||||
if (new RegExp('^' + article + ' ').test(sortValue)) {
|
||||
sortValue = trim(sortValue.slice(article.length + 1))
|
||||
+ ', ' + sortValue.slice(0, article.length);
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
} else {
|
||||
sort[value] = mappedValue;
|
||||
}
|
||||
// pad numbers
|
||||
sortValue = sortValue.replace(/\d+/g, function(match) {
|
||||
return Ox.pad(match, 'left', 64, '0');
|
||||
});
|
||||
return sort;
|
||||
}
|
||||
return sortValue;
|
||||
});
|
||||
|
||||
/*@
|
||||
Ox.sort <f> Sorts an array, handling articles and digits, ignoring capitalization
|
||||
|
@ -637,15 +634,9 @@ Ox.range = function() {
|
|||
Ox.sort = function(array, map) {
|
||||
var values = getSortValues(map ? array.map(map) : array);
|
||||
return array.sort(function(a, b) {
|
||||
a = map ? map(a) : a;
|
||||
b = map ? map(b) : b;
|
||||
var ret = 0;
|
||||
if (values[a] < values[b]) {
|
||||
ret = -1;
|
||||
} else if (values[a] > values[b]) {
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
a = getSortValue(map ? map(a) : a);
|
||||
b = getSortValue(map ? map(b) : b);
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -661,28 +652,24 @@ Ox.range = function() {
|
|||
[{id: 1, name: '8 Women'}, {id: 0, name: '80 Days'}]
|
||||
@*/
|
||||
Ox.sortBy = function(array, by, map) {
|
||||
var values = {};
|
||||
by = Ox.makeArray(by);
|
||||
map = map || {};
|
||||
by = by.map(function(value) {
|
||||
var sortValues = {};
|
||||
by = Ox.makeArray(by).map(function(value) {
|
||||
return Ox.isString(value) ? {
|
||||
key: value.replace(/^[\+\-]/, ''),
|
||||
operator: value[0] == '-' ? '-' : '+'
|
||||
} : value;
|
||||
});
|
||||
by.map(function(value) {
|
||||
return value.key;
|
||||
}).forEach(function(key) {
|
||||
values[key] = getSortValues(array.map(function(value) {
|
||||
return value[key];
|
||||
}), map[key]);
|
||||
});
|
||||
map = map || {};
|
||||
return array.sort(function(a, b) {
|
||||
var aValue, bValue, index = 0, key, ret = 0;
|
||||
while (ret == 0 && index < by.length) {
|
||||
key = by[index].key;
|
||||
aValue = values[key][a[key]];
|
||||
bValue = values[key][b[key]];
|
||||
aValue = getSortValue(
|
||||
map[key] ? map[key](a[key], a) : a[key]
|
||||
);
|
||||
bValue = getSortValue(
|
||||
map[key] ? map[key](b[key], b) : b[key]
|
||||
);
|
||||
if ((aValue === null) != (bValue === null)) {
|
||||
ret = aValue === null ? 1 : -1;
|
||||
} else if (aValue < bValue) {
|
||||
|
|
Loading…
Reference in a new issue