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:
rolux 2012-09-03 22:11:25 +02:00
parent baf29d068b
commit f5c320187e

View file

@ -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) {