mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-28 13:17:42 +00:00
Merge remote-tracking branch 'upstream/master' into CPTableViewLion
This commit is contained in:
@@ -334,7 +334,16 @@
|
||||
|
||||
- (void)sortUsingDescriptors:(CPArray)descriptors
|
||||
{
|
||||
[self sortUsingFunction:compareObjectsUsingDescriptors context:descriptors];
|
||||
var i = [descriptors count],
|
||||
jsDescriptors = [];
|
||||
|
||||
// Revert the order of the descriptors
|
||||
while (i--)
|
||||
{
|
||||
var d = [descriptors objectAtIndex:i];
|
||||
[jsDescriptors addObject:{ "k": [d key], "a": [d ascending], "s": [d selector]}];
|
||||
}
|
||||
sortArrayUsingJSDescriptors(self, jsDescriptors);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -344,46 +353,7 @@
|
||||
*/
|
||||
- (void)sortUsingFunction:(Function)aFunction context:(id)aContext
|
||||
{
|
||||
var h,
|
||||
i,
|
||||
j,
|
||||
k,
|
||||
l,
|
||||
m,
|
||||
n = [self count],
|
||||
o;
|
||||
|
||||
var A,
|
||||
B = [];
|
||||
|
||||
for (h = 1; h < n; h += h)
|
||||
{
|
||||
for (m = n - 1 - h; m >= 0; m -= h + h)
|
||||
{
|
||||
l = m - h + 1;
|
||||
if (l < 0)
|
||||
l = 0;
|
||||
|
||||
for (i = 0, j = l; j <= m; i++, j++)
|
||||
B[i] = self[j];
|
||||
|
||||
for (i = 0, k = l; k < j && j <= m + h; k++)
|
||||
{
|
||||
A = self[j];
|
||||
o = aFunction(A, B[i], aContext);
|
||||
if (o >= 0)
|
||||
self[k] = B[i++];
|
||||
else
|
||||
{
|
||||
self[k] = A;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
while (k < j)
|
||||
self[k++] = B[i++];
|
||||
}
|
||||
}
|
||||
sortArrayUsingFunction(self, aFunction, aContext);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -392,7 +362,7 @@
|
||||
*/
|
||||
- (void)sortUsingSelector:(SEL)aSelector
|
||||
{
|
||||
[self sortUsingFunction:selectorCompare context:aSelector];
|
||||
sortArrayUsingFunction(self, selectorCompare, aSelector);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -408,20 +378,163 @@
|
||||
|
||||
@end
|
||||
|
||||
var selectorCompare = function selectorCompare(object1, object2, selector)
|
||||
var selectorCompare = function(object1, object2, selector)
|
||||
{
|
||||
return [object1 performSelector:selector withObject:object2];
|
||||
};
|
||||
|
||||
// sort using sort descriptors
|
||||
var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, rhs, descriptors)
|
||||
var sortArrayUsingFunction = function(array, aFunction, aContext)
|
||||
{
|
||||
var result = CPOrderedSame,
|
||||
i = 0,
|
||||
n = [descriptors count];
|
||||
var h,
|
||||
i,
|
||||
j,
|
||||
k,
|
||||
l,
|
||||
m,
|
||||
n = array.length,
|
||||
o;
|
||||
|
||||
while (i < n && result === CPOrderedSame)
|
||||
result = [descriptors[i++] compareObject:lhs withObject:rhs];
|
||||
var A,
|
||||
B = [];
|
||||
|
||||
return result;
|
||||
};
|
||||
for (h = 1; h < n; h += h)
|
||||
{
|
||||
for (m = n - 1 - h; m >= 0; m -= h + h)
|
||||
{
|
||||
l = m - h + 1;
|
||||
if (l < 0)
|
||||
l = 0;
|
||||
|
||||
for (i = 0, j = l; j <= m; i++, j++)
|
||||
B[i] = array[j];
|
||||
|
||||
for (i = 0, k = l; k < j && j <= m + h; k++)
|
||||
{
|
||||
A = array[j];
|
||||
o = aFunction(A, B[i], aContext);
|
||||
|
||||
if (o >= 0)
|
||||
array[k] = B[i++];
|
||||
else
|
||||
{
|
||||
array[k] = A;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
while (k < j)
|
||||
array[k++] = B[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Observe that the sort descriptors has the reversed order by the caller
|
||||
var sortArrayUsingJSDescriptors = function(a, d)
|
||||
{
|
||||
var h,
|
||||
i,
|
||||
j,
|
||||
k,
|
||||
l,
|
||||
m,
|
||||
n = a.length,
|
||||
dl = d.length - 1,
|
||||
o,
|
||||
c = {};
|
||||
|
||||
var A,
|
||||
B = [],
|
||||
C1,
|
||||
C2,
|
||||
cn,
|
||||
aUID,
|
||||
bUID,
|
||||
key,
|
||||
dd;
|
||||
|
||||
if (dl < 0)
|
||||
return;
|
||||
|
||||
for (h = 1; h < n; h += h)
|
||||
{
|
||||
for (m = n - 1 - h; m >= 0; m -= h + h)
|
||||
{
|
||||
l = m - h + 1;
|
||||
|
||||
if (l < 0)
|
||||
l = 0;
|
||||
|
||||
for (i = 0, j = l; j <= m; i++, j++)
|
||||
B[i] = a[j];
|
||||
|
||||
for (i = 0, k = l; k < j && j <= m + h; k++)
|
||||
{
|
||||
A = a[j];
|
||||
aUID = A._UID;
|
||||
|
||||
if (!aUID)
|
||||
aUID = [A UID];
|
||||
|
||||
C1 = c[aUID];
|
||||
|
||||
if (!C1)
|
||||
{
|
||||
C1 = {};
|
||||
cn = dl;
|
||||
|
||||
do
|
||||
{
|
||||
key = d[cn].k;
|
||||
C1[key] = [A valueForKey:key];
|
||||
} while (cn--)
|
||||
|
||||
c[aUID] = C1;
|
||||
}
|
||||
|
||||
bUID = B[i]._UID;
|
||||
|
||||
if (!bUID)
|
||||
bUID = [B[i] UID];
|
||||
|
||||
C2 = c[bUID];
|
||||
|
||||
if (!C2)
|
||||
{
|
||||
C2 = {};
|
||||
cn = dl;
|
||||
|
||||
do
|
||||
{
|
||||
key = d[cn].k;
|
||||
C2[key] = [B[i] valueForKey:key];
|
||||
} while (cn--)
|
||||
|
||||
c[bUID] = C2;
|
||||
}
|
||||
|
||||
cn = dl;
|
||||
|
||||
do
|
||||
{
|
||||
dd = d[cn];
|
||||
key = dd.k;
|
||||
o = objj_msgSend(C1[key], dd.s, C2[key]);
|
||||
|
||||
if (o && !dd.a)
|
||||
o = -o;
|
||||
} while (cn-- && o == CPOrderedSame)
|
||||
|
||||
if (o >= 0)
|
||||
a[k] = B[i++];
|
||||
else
|
||||
{
|
||||
a[k] = A;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
while (k < j)
|
||||
a[k++] = B[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,136 @@
|
||||
var FILE = require("file");
|
||||
|
||||
@import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPString.j>
|
||||
@import <Foundation/CPNumber.j>
|
||||
@import <Foundation/CPSortDescriptor.j>
|
||||
|
||||
|
||||
var ELEMENTS = 100,
|
||||
REPEATS = 10;
|
||||
|
||||
@implementation CPArrayPerformanceTest : OJTestCase
|
||||
|
||||
- (void)testSortUsingDescriptorsSpeed
|
||||
{
|
||||
var array = [self makeUnsorted];
|
||||
|
||||
var descriptors = [
|
||||
[CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO],
|
||||
[CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES]
|
||||
];
|
||||
|
||||
var start = (new Date).getTime();
|
||||
|
||||
for (var i = 0; i < REPEATS; ++i)
|
||||
{
|
||||
var sorted = [array sortedArrayUsingDescriptors:descriptors];
|
||||
|
||||
[self checkSorted:sorted];
|
||||
}
|
||||
|
||||
var end = (new Date).getTime();
|
||||
|
||||
CPLog.warn(_cmd + ": " + (end - start) + "ms");
|
||||
CPArray descriptors;
|
||||
}
|
||||
|
||||
- (void)testSortUsingNativeSort
|
||||
- (void)setUp
|
||||
{
|
||||
var array = [self makeUnsorted];
|
||||
|
||||
var descriptors = [
|
||||
[CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO],
|
||||
[CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES]
|
||||
];
|
||||
[CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO],
|
||||
[CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES]
|
||||
];
|
||||
}
|
||||
|
||||
function sortFunction(lhs, rhs)
|
||||
{
|
||||
return ([lhs a] === [rhs a]) ? ([lhs b] - [rhs b]) : ([rhs a] - [lhs a]);
|
||||
}
|
||||
- (void)testAlmostSortedNumericUsingMergeSort
|
||||
{
|
||||
CPLog.warn("\nNUMERIC ALMOST SORTED");
|
||||
var a = [self makeUnsorted],
|
||||
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
|
||||
[self checkAlmostSorted:sorted];
|
||||
}
|
||||
|
||||
var start = (new Date).getTime();
|
||||
- (void)testAlmostSortedNumericUsingNativeSort
|
||||
{
|
||||
var a = [self makeUnsorted],
|
||||
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
|
||||
[self checkAlmostSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testRandomNumericUsingMergeSort
|
||||
{
|
||||
CPLog.warn("\nNUMERIC RANDOM");
|
||||
var a = [self makeRandomNumeric],
|
||||
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
|
||||
[self checkRandomSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testRandomNumericUsingNativeSort
|
||||
{
|
||||
var a = [self makeRandomNumeric],
|
||||
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
|
||||
[self checkRandomSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testRandomTextUsingMergeSort
|
||||
{
|
||||
CPLog.warn("\nTEXT RANDOM");
|
||||
var a = [self makeRandomText],
|
||||
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
|
||||
[self checkRandomSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testRandomTextUsingNativeSort
|
||||
{
|
||||
var a = [self makeRandomText],
|
||||
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
|
||||
[self checkRandomSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testAlmostSortedNumericUsingMergeSelectorSort
|
||||
{
|
||||
CPLog.warn("\nNUMERIC ALMOST SORTED (SELECTOR)");
|
||||
var a = [self makeUnsorted],
|
||||
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
|
||||
[self checkAlmostSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testAlmostSortedNumericUsingNativeSelectorSort
|
||||
{
|
||||
var a = [self makeUnsorted],
|
||||
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
|
||||
[self checkAlmostSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testRandomNumericUsingMergeSelectorSort
|
||||
{
|
||||
CPLog.warn("\nNUMERIC RANDOM (SELECTOR)");
|
||||
var a = [self makeRandomNumeric],
|
||||
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
|
||||
[self checkRandomSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testRandomNumericUsingNativeSelectorSort
|
||||
{
|
||||
var a = [self makeRandomNumeric],
|
||||
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
|
||||
[self checkRandomSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testRandomTextUsingMergeSelectorSort
|
||||
{
|
||||
CPLog.warn("\nTEXT RANDOM (SELECTOR)");
|
||||
var a = [self makeRandomText],
|
||||
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
|
||||
[self checkRandomSorted:sorted];
|
||||
}
|
||||
|
||||
- (void)testRandomTextUsingNativeSelectorSort
|
||||
{
|
||||
var a = [self makeRandomText],
|
||||
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
|
||||
[self checkRandomSorted:sorted];
|
||||
}
|
||||
|
||||
- (CPArray)sort:(CPArray)anArray usingSortSelector:(SEL)aSelector withObject:(id)anObject
|
||||
{
|
||||
var sorted,
|
||||
start = (new Date).getTime();
|
||||
|
||||
for (var i = 0; i < REPEATS; ++i)
|
||||
{
|
||||
var sorted = [array copy];
|
||||
|
||||
sorted.sort(sortFunction)
|
||||
|
||||
[self checkSorted:sorted];
|
||||
}
|
||||
sorted = [anArray performSelector:aSelector withObject:anObject];
|
||||
|
||||
var end = (new Date).getTime();
|
||||
|
||||
CPLog.warn(_cmd+": " + (end - start) + "ms");
|
||||
CPLog.warn(aSelector + ": " + (end - start) + "ms");
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
- (CPArray)makeUnsorted
|
||||
{
|
||||
var array = [];
|
||||
|
||||
for (var i=0; i < ELEMENTS; ++i)
|
||||
for (var i = 0; i < ELEMENTS; ++i)
|
||||
{
|
||||
var s = [Sortable new];
|
||||
|
||||
@@ -79,12 +143,49 @@ var ELEMENTS = 100,
|
||||
return array;
|
||||
}
|
||||
|
||||
- (void)checkSorted:(CPArray)sorted
|
||||
- (CPArray)makeRandomNumeric
|
||||
{
|
||||
var array = [];
|
||||
|
||||
for (var i = 0; i < ELEMENTS; i++)
|
||||
{
|
||||
var s = [Sortable new],
|
||||
n1 = ROUND(RAND() * ELEMENTS),
|
||||
n2 = ROUND(RAND() * ELEMENTS);
|
||||
|
||||
[s setA:n1];
|
||||
[s setB:n2];
|
||||
array.push(s);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
- (CPArray)makeRandomText
|
||||
{
|
||||
var the_big_sort = FILE.read(FILE.join(FILE.dirname(module.path), "the_big_sort.txt"), { charset:"UTF-8" }),
|
||||
words = the_big_sort.split(" ", ELEMENTS),
|
||||
wordcount = words.length,
|
||||
array = [];
|
||||
|
||||
for (var i = 0; i < wordcount - 1; i++)
|
||||
{
|
||||
var s = [Sortable new];
|
||||
|
||||
[s setA:words[i]];
|
||||
[s setB:words[i + 1]];
|
||||
array.push(s);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
- (void)checkAlmostSorted:(CPArray)sorted
|
||||
{
|
||||
// Verify it really got sorted.
|
||||
for (var j=0; j < ELEMENTS; ++j)
|
||||
for (var j = 0; j < sorted.length; ++j)
|
||||
{
|
||||
var expectedA = 4-FLOOR(j * 5 / ELEMENTS);
|
||||
var expectedA = 4 - FLOOR(j * 5 / ELEMENTS);
|
||||
|
||||
if (sorted[j].a != expectedA)
|
||||
[self fail:"a out of order: " + expectedA + " != " + sorted[j].a];
|
||||
@@ -96,6 +197,66 @@ var ELEMENTS = 100,
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkRandomSorted:(CPArray)sorted
|
||||
{
|
||||
// Verify that it really got sorted.
|
||||
for (var j = 0; j < sorted.length - 1; ++j)
|
||||
{
|
||||
if ([sorted[j].a compare:sorted[j + 1].a] == CPOrderedAscending)
|
||||
[self fail:"a out of order: " + sorted[j].a + " > " + sorted[j + 1].a];
|
||||
|
||||
if ([sorted[j].a compare:sorted[j + 1].a] == CPOrderedSame && [sorted[j].b compare: sorted[j + 1].b] == CPOrderedDescending)
|
||||
[self fail:"b out of order: " + sorted[j].b + " < " + sorted[j + 1].b];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPArray (NativeSort)
|
||||
|
||||
- (CPArray)_native_sortedArrayUsingDescriptors:(CPArray)descriptors
|
||||
{
|
||||
var sorted = [self copy];
|
||||
|
||||
[sorted _native_sortUsingDescriptors:descriptors];
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
- (CPArray)_native_sortUsingDescriptors:(CPArray)descriptors
|
||||
{
|
||||
var count = [descriptors count];
|
||||
|
||||
sort(function(lhs, rhs)
|
||||
{
|
||||
var i = 0,
|
||||
result = CPOrderedSame;
|
||||
|
||||
while (i < count)
|
||||
if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) !== CPOrderedSame)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
- (CPArray)_native_sortedArrayUsingSelector:(SEL)aSelector
|
||||
{
|
||||
var sorted = [self copy];
|
||||
|
||||
[sorted _native_sortUsingSelector:aSelector];
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
- (CPArray)_native_sortUsingSelector:(SEL)aSelector
|
||||
{
|
||||
sort(function(lhs, rhs)
|
||||
{
|
||||
return [lhs performSelector:aSelector withObject:rhs];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation Sortable : CPObject
|
||||
@@ -104,4 +265,11 @@ var ELEMENTS = 100,
|
||||
int b @accessors;
|
||||
}
|
||||
|
||||
- (int)compareAAscendingThenBDescending:(Sortable)other
|
||||
{
|
||||
var aResult = [a compare:other.a];
|
||||
|
||||
return aResult === CPOrderedSame ? [b compare:other.b] : -aResult;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -539,6 +539,20 @@
|
||||
[self assertTrue:d.indexOf("y: 6") !== -1 message:"Can't find 'y: 6' in description of array " + d];
|
||||
}
|
||||
|
||||
- (void)testSortUsingDescriptorsWithDifferentSelectors
|
||||
{
|
||||
var a = [CPDictionary dictionaryWithJSObject:{"a": "AB", "b": "ba"}],
|
||||
b = [CPDictionary dictionaryWithJSObject:{"a": "aa", "b": "BB"}],
|
||||
array = [a, b],
|
||||
d1 = [[CPSortDescriptor sortDescriptorWithKey:@"a" ascending:YES selector:@selector(compare:)]],
|
||||
d2 = [[CPSortDescriptor sortDescriptorWithKey:@"a" ascending:YES selector:@selector(caseInsensitiveCompare:)]],
|
||||
s1 = [array sortedArrayUsingDescriptors:d1],
|
||||
s2 = [array sortedArrayUsingDescriptors:d2];
|
||||
|
||||
[self assertTrue:s1[0] === a message:s1[0] + " is larger then " + a + " when sorting case sensitive"];
|
||||
[self assertTrue:s2[1] === a message:s2[1] + " is larger then " + a + " when sorting case insensitive"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation AlwaysEqual : CPObject
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user