Skip to content

Commit 120e283

Browse files
authored
Sort undefined according to the comparison function (#195)
1 parent c5ae83f commit 120e283

File tree

9 files changed

+180
-27
lines changed

9 files changed

+180
-27
lines changed

src/Data/Array.js

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,65 @@ exports.scanr = function (f) {
270270
// Sorting ---------------------------------------------------------------------
271271
//------------------------------------------------------------------------------
272272

273-
exports.sortImpl = function (f) {
274-
return function (l) {
275-
return l.slice().sort(function (x, y) {
276-
return f(x)(y);
277-
});
273+
exports.sortByImpl = (function () {
274+
function mergeFromTo(compare, fromOrdering, xs1, xs2, from, to) {
275+
var mid;
276+
var i;
277+
var j;
278+
var k;
279+
var x;
280+
var y;
281+
var c;
282+
283+
mid = from + ((to - from) >> 1);
284+
if (mid - from > 1) mergeFromTo(compare, fromOrdering, xs2, xs1, from, mid);
285+
if (to - mid > 1) mergeFromTo(compare, fromOrdering, xs2, xs1, mid, to);
286+
287+
i = from;
288+
j = mid;
289+
k = from;
290+
while (i < mid && j < to) {
291+
x = xs2[i];
292+
y = xs2[j];
293+
c = fromOrdering(compare(x)(y));
294+
if (c > 0) {
295+
xs1[k++] = y;
296+
++j;
297+
}
298+
else if (c === 0) {
299+
xs1[k++] = x;
300+
xs1[k++] = y;
301+
++i;
302+
++j;
303+
}
304+
else {
305+
xs1[k++] = x;
306+
++i;
307+
}
308+
}
309+
while (i < mid) {
310+
xs1[k++] = xs2[i++];
311+
}
312+
while (j < to) {
313+
xs1[k++] = xs2[j++];
314+
}
315+
}
316+
317+
return function (compare) {
318+
return function (fromOrdering) {
319+
return function (xs) {
320+
var out;
321+
322+
if (xs.length < 2) return xs;
323+
324+
out = xs.slice(0);
325+
mergeFromTo(compare, fromOrdering, out, xs.slice(0), 0, xs.length);
326+
327+
return out;
328+
};
329+
};
278330
};
279-
};
331+
})();
280332

281333
//------------------------------------------------------------------------------
282334
// Subarrays -------------------------------------------------------------------

src/Data/Array.purs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -801,12 +801,10 @@ sort xs = sortBy compare xs
801801
-- | ```
802802
-- |
803803
sortBy :: forall a. (a -> a -> Ordering) -> Array a -> Array a
804-
sortBy comp xs = sortImpl comp' xs
805-
where
806-
comp' x y = case comp x y of
807-
GT -> 1
808-
EQ -> 0
809-
LT -> -1
804+
sortBy comp = sortByImpl comp case _ of
805+
GT -> 1
806+
EQ -> 0
807+
LT -> -1
810808

811809
-- | Sort the elements of an array in increasing order, where elements are
812810
-- | sorted based on a projection
@@ -819,7 +817,7 @@ sortBy comp xs = sortImpl comp' xs
819817
sortWith :: forall a b. Ord b => (a -> b) -> Array a -> Array a
820818
sortWith f = sortBy (comparing f)
821819

822-
foreign import sortImpl :: forall a. (a -> a -> Int) -> Array a -> Array a
820+
foreign import sortByImpl :: forall a. (a -> a -> Ordering) -> (Ordering -> Int) -> Array a -> Array a
823821

824822
--------------------------------------------------------------------------------
825823
-- Subarrays -------------------------------------------------------------------

src/Data/Array/ST.js

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,64 @@ exports.freeze = copyImpl;
9898

9999
exports.thaw = copyImpl;
100100

101-
exports.sortByImpl = function (comp) {
102-
return function (xs) {
103-
return function () {
104-
return xs.sort(function (x, y) {
105-
return comp(x)(y);
106-
});
101+
exports.sortByImpl = (function () {
102+
function mergeFromTo(compare, fromOrdering, xs1, xs2, from, to) {
103+
var mid;
104+
var i;
105+
var j;
106+
var k;
107+
var x;
108+
var y;
109+
var c;
110+
111+
mid = from + ((to - from) >> 1);
112+
if (mid - from > 1) mergeFromTo(compare, fromOrdering, xs2, xs1, from, mid);
113+
if (to - mid > 1) mergeFromTo(compare, fromOrdering, xs2, xs1, mid, to);
114+
115+
i = from;
116+
j = mid;
117+
k = from;
118+
while (i < mid && j < to) {
119+
x = xs2[i];
120+
y = xs2[j];
121+
c = fromOrdering(compare(x)(y));
122+
if (c > 0) {
123+
xs1[k++] = y;
124+
++j;
125+
}
126+
else if (c === 0) {
127+
xs1[k++] = x;
128+
xs1[k++] = y;
129+
++i;
130+
++j;
131+
}
132+
else {
133+
xs1[k++] = x;
134+
++i;
135+
}
136+
}
137+
while (i < mid) {
138+
xs1[k++] = xs2[i++];
139+
}
140+
while (j < to) {
141+
xs1[k++] = xs2[j++];
142+
}
143+
}
144+
145+
return function (compare) {
146+
return function (fromOrdering) {
147+
return function (xs) {
148+
return function () {
149+
if (xs.length < 2) return xs;
150+
151+
mergeFromTo(compare, fromOrdering, xs, xs.slice(0), 0, xs.length);
152+
153+
return xs;
154+
};
155+
};
107156
};
108157
};
109-
};
158+
})();
110159

111160
exports.toAssocArray = function (xs) {
112161
return function () {

src/Data/Array/ST.purs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,15 @@ sortBy
102102
. (a -> a -> Ordering)
103103
-> STArray h a
104104
-> ST h (STArray h a)
105-
sortBy comp = sortByImpl comp'
106-
where
107-
comp' x y = case comp x y of
108-
GT -> 1
109-
EQ -> 0
110-
LT -> -1
105+
sortBy comp = sortByImpl comp case _ of
106+
GT -> 1
107+
EQ -> 0
108+
LT -> -1
111109

112110
foreign import sortByImpl
113111
:: forall a h
114-
. (a -> a -> Int)
112+
. (a -> a -> Ordering)
113+
-> (Ordering -> Int)
115114
-> STArray h a
116115
-> ST h (STArray h a)
117116

test/Test/Data/Array.purs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Effect (Effect)
1515
import Effect.Console (log)
1616
import Partial.Unsafe (unsafePartial)
1717
import Test.Assert (assert)
18+
import Test.Data.UndefinedOr (defined, undefined)
1819

1920
testArray :: Effect Unit
2021
testArray = do
@@ -281,6 +282,7 @@ testArray = do
281282

282283
log "sort should reorder a list into ascending order based on the result of compare"
283284
assert $ A.sort [1, 3, 2, 5, 6, 4] == [1, 2, 3, 4, 5, 6]
285+
assert $ A.sort [defined 1, undefined, defined 2] == [undefined, defined 1, defined 2]
284286

285287
log "sortBy should reorder a list into ascending order based on the result of a comparison function"
286288
assert $ A.sortBy (flip compare) [1, 3, 2, 5, 6, 4] == [6, 5, 4, 3, 2, 1]

test/Test/Data/Array/NonEmpty.purs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Effect (Effect)
1818
import Effect.Console (log)
1919
import Partial.Unsafe (unsafePartial)
2020
import Test.Assert (assert)
21+
import Test.Data.UndefinedOr (defined, undefined)
2122

2223
testNonEmptyArray :: Effect Unit
2324
testNonEmptyArray = do
@@ -202,6 +203,7 @@ testNonEmptyArray = do
202203

203204
log "sort should reorder a list into ascending order based on the result of compare"
204205
assert $ NEA.sort (fromArray [1, 3, 2, 5, 6, 4]) == fromArray [1, 2, 3, 4, 5, 6]
206+
assert $ NEA.sort (fromArray [defined 1, undefined, defined 2]) == fromArray [undefined, defined 1, defined 2]
205207

206208
log "sortBy should reorder a list into ascending order based on the result of a comparison function"
207209
assert $ NEA.sortBy (flip compare) (fromArray [1, 3, 2, 5, 6, 4]) == fromArray [6, 5, 4, 3, 2, 1]

test/Test/Data/Array/ST.purs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Data.Maybe (Maybe(..), isNothing)
1010
import Effect (Effect)
1111
import Effect.Console (log)
1212
import Test.Assert (assert)
13+
import Test.Data.UndefinedOr (defined, undefined)
1314

1415
testArrayST :: Effect Unit
1516
testArrayST = do
@@ -224,6 +225,9 @@ testArrayST = do
224225
assert $ STA.run (
225226
STA.sort =<< STA.unsafeThaw [1, 3, 2, 5, 6, 4]
226227
) == [1, 2, 3, 4, 5, 6]
228+
assert $ STA.run (
229+
STA.sort =<< STA.unsafeThaw [defined 1, undefined, defined 2]
230+
) == [undefined, defined 1, defined 2]
227231

228232
log "sortBy should reorder a list into ascending order based on the result of a comparison function"
229233
assert $ STA.run (

test/Test/Data/UndefinedOr.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
exports.undefined = undefined;
2+
3+
exports.defined = function (x) {
4+
return x;
5+
};
6+
7+
exports.eqUndefinedOrImpl = function (eq) {
8+
return function (a) {
9+
return function (b) {
10+
return (a === undefined && b === undefined) || eq(a)(b);
11+
};
12+
};
13+
};
14+
15+
exports.compareUndefinedOrImpl = function (lt) {
16+
return function (eq) {
17+
return function (gt) {
18+
return function (compare) {
19+
return function (a) {
20+
return function (b) {
21+
if (a === undefined && b === undefined) return eq;
22+
if (a === undefined) return lt;
23+
if (b === undefined) return gt;
24+
return compare(a)(b);
25+
};
26+
};
27+
};
28+
};
29+
};
30+
};

test/Test/Data/UndefinedOr.purs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Test.Data.UndefinedOr where
2+
3+
import Prelude
4+
5+
foreign import data UndefinedOr :: Type -> Type
6+
7+
foreign import undefined :: forall a. UndefinedOr a
8+
foreign import defined :: forall a. a -> UndefinedOr a
9+
10+
foreign import eqUndefinedOrImpl :: forall a. (a -> a -> Boolean) -> UndefinedOr a -> UndefinedOr a -> Boolean
11+
foreign import compareUndefinedOrImpl :: forall a. Ordering -> Ordering -> Ordering -> (a -> a -> Ordering) -> UndefinedOr a -> UndefinedOr a -> Ordering
12+
13+
instance eqUndefinedOr :: Eq a => Eq (UndefinedOr a) where
14+
eq = eqUndefinedOrImpl eq
15+
16+
instance ordUndefinedOr :: Ord a => Ord (UndefinedOr a) where
17+
compare = compareUndefinedOrImpl LT EQ GT compare

0 commit comments

Comments
 (0)