Repository: scikit-hep/awkward

Issue:
Remember to add a high-level interface to sorting (ak.sort)
It should NEP 18 to [np.sort](https://numpy.org/doc/1.18/reference/generated/numpy.sort.html). There's no equivalent for our `ascending` or their `order`, though `kind` can be partially supported because `kind="stable"` corresponds to our `stable=True`. (Is our `stable=False` one of "quicksort", "mergesort", "heapsort"?)

I meant to include this in #168, but forgot.


Discussion:
**C++’s sort()** – Introsort (Hybrid of Quicksort, Heap Sort and Insertion Sort)

- Best Case Time Complexity- O(NlogN)
- Average Case Time Complexity- O(NlogN)
- Worse Case Time Complexity- O(NlogN)
- Auxiliary Space- O(logN)
- Stable- No
- Adaptive- No

**C++’s stable_sort()** – Mergesort

- Best Case Time Complexity- O(NlogN)
- Average Case Time Complexity- O(NlogN)
- Worse Case Time Complexity- O(NlogN)
- Auxiliary Space- O(N)
- Stable- Yes
- Adaptive- Yes

Resolving patch:
diff --git a/src/awkward1/operations/structure.py b/src/awkward1/operations/structure.py
--- a/src/awkward1/operations/structure.py
+++ b/src/awkward1/operations/structure.py
@@ -1014,6 +1014,85 @@ def apply(layout):
         return out
 
 
+@awkward1._connect._numpy.implements(numpy.sort)
+def sort(array, axis=-1, ascending=True, stable=True, highlevel=True):
+    """
+    Args:
+        array: Data to sort, possibly within nested lists.
+        axis (int): The dimension at which this operation is applied. The
+            outermost dimension is `0`, followed by `1`, etc., and negative
+            values count backward from the innermost: `-1` is the innermost
+            dimension, `-2` is the next level up, etc.
+        ascending (bool): If True, the first value in each sorted group
+            will be smallest, the last value largest; if False, the order
+            is from largest to smallest.
+        stable (bool): If True, use a stable sorting algorithm (introsort:
+            a hybrid of quicksort, heapsort, and insertion sort); if False,
+            use a sorting algorithm that is not guaranteed to be stable
+            (heapsort).
+        highlevel (bool): If True, return an #ak.Array; otherwise, return
+            a low-level #ak.layout.Content subclass.
+
+    For example,
+
+        >>> ak.sort(ak.Array([[7, 5, 7], [], [2], [8, 2]]))
+        <Array [[5, 7, 7], [], [2], [2, 8]] type='4 * var * int64'>
+    """
+    layout = awkward1.operations.convert.to_layout(
+        array, allow_record=False, allow_other=False
+    )
+    out = layout.sort(axis, ascending, stable)
+    if highlevel:
+        return awkward1._util.wrap(out, awkward1._util.behaviorof(array))
+    else:
+        return out
+
+
+@awkward1._connect._numpy.implements(numpy.argsort)
+def argsort(array, axis=-1, ascending=True, stable=True, highlevel=True):
+    """
+    Args:
+        array: Data for which to get a sorting index, possibly within nested
+            lists.
+        axis (int): The dimension at which this operation is applied. The
+            outermost dimension is `0`, followed by `1`, etc., and negative
+            values count backward from the innermost: `-1` is the innermost
+            dimension, `-2` is the next level up, etc.
+        ascending (bool): If True, the first value in each sorted group
+            will be smallest, the last value largest; if False, the order
+            is from largest to smallest.
+        stable (bool): If True, use a stable sorting algorithm (introsort:
+            a hybrid of quicksort, heapsort, and insertion sort); if False,
+            use a sorting algorithm that is not guaranteed to be stable
+            (heapsort).
+        highlevel (bool): If True, return an #ak.Array; otherwise, return
+            a low-level #ak.layout.Content subclass.
+
+    For example,
+
+        >>> ak.argsort(ak.Array([[7.7, 5.5, 7.7], [], [2.2], [8.8, 2.2]]))
+        <Array [[1, 0, 2], [], [0], [1, 0]] type='4 * var * int64'>
+
+    The result of this function can be used to index other arrays with the
+    same shape:
+
+        >>> data = ak.Array([[7, 5, 7], [], [2], [8, 2]])
+        >>> index = ak.argsort(index)
+        >>> index
+        <Array [[1, 0, 2], [], [0], [1, 0]] type='4 * var * int64'>
+        >>> data[index]
+        <Array [[5, 7, 7], [], [2], [2, 8]] type='4 * var * int64'>
+    """
+    layout = awkward1.operations.convert.to_layout(
+        array, allow_record=False, allow_other=False
+    )
+    out = layout.argsort(axis, ascending, stable)
+    if highlevel:
+        return awkward1._util.wrap(out, awkward1._util.behaviorof(array))
+    else:
+        return out
+
+
 def pad_none(array, target, axis=1, clip=False, highlevel=True):
     """
     Args:


Test patch:
diff --git a/tests/test_0339-highlevel-sorting-function.py b/tests/test_0339-highlevel-sorting-function.py
new file mode 100644
--- /dev/null
+++ b/tests/test_0339-highlevel-sorting-function.py
@@ -0,0 +1,17 @@
+# BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/master/LICENSE
+
+from __future__ import absolute_import
+
+import sys
+
+import pytest
+import numpy
+
+import awkward1
+
+def test():
+    data = awkward1.Array([[7, 5, 7], [], [2], [8, 2]])
+    assert awkward1.to_list(awkward1.sort(data)) == [[5, 7, 7], [], [2], [2, 8]]
+
+    index = awkward1.argsort(data)
+    assert awkward1.to_list(data[index]) == [[5, 7, 7], [], [2], [2, 8]]