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