Repository: numpy/numpy
File: numpy/lib/_arraysetops_impl.py (Python)

Code before:
              axis=None, sorted=True):
    """
    Find the unique elements of an array, ignoring shape.

    Uses a hash table to find the unique elements if possible.
    """
    ar = np.asanyarray(ar).flatten()
    if len(ar.shape) != 1:
        # np.matrix, and maybe some other array subclasses, insist on keeping
        # two dimensions for all operations. Coerce to an ndarray in such cases.
        ar = np.asarray(ar).flatten()

    optional_indices = return_index or return_inverse

    if (optional_indices or return_counts) and not sorted:
        raise ValueError(
            "`sorted` can only be False if `return_index`, `return_inverse`, "
            "and `return_counts` are all False."
        )

    # masked arrays are not supported yet.
    if not optional_indices and not return_counts and not np.ma.is_masked(ar):
        # from numpy._core._multiarray_umath import unique_hash
        from numpy._core._unique import unique_hash
        hash_unique = unique_hash(ar)
        if hash_unique is not None:
            if sorted:
                hash_unique.sort()
            return (hash_unique,)


    if optional_indices:
        perm = ar.argsort(kind='mergesort' if return_index else 'quicksort')
        aux = ar[perm]
    else:
        ar.sort()
        aux = ar
    mask = np.empty(aux.shape, dtype=np.bool)
    mask[:1] = True
    if (equal_nan and aux.shape[0] > 0 and aux.dtype.kind in "cfmM" and
            np.isnan(aux[-1])):
        if aux.dtype.kind == "c":  # for complex all NaNs are considered equivalent
            aux_firstnan = np.searchsorted(np.isnan(aux), True, side='left')
        else:
            aux_firstnan = np.searchsorted(aux, aux[-1], side='left')
        if aux_firstnan > 0:
            mask[1:aux_firstnan] = (
                aux[1:aux_firstnan] != aux[:aux_firstnan - 1])
        mask[aux_firstnan] = True
        mask[aux_firstnan + 1:] = False
    else:

Review comment (suggestion):
Either here, or in C, can use a pattern of:
```
conv = _array_converter(arr)
arr, = conv

return conv.wrap(result)
```
to ensure we call `__array_wrap__` but work with base-class arrays.  Could also do that in C, but here is probably nicer.

(Grep for `conv.wrap`, a test with a trivial sublcass might be nice.)

Code after:

def _unique1d(ar, return_index=False, return_inverse=False,
              return_counts=False, *, equal_nan=True, inverse_shape=None,
              axis=None, sorted=True):
    """
    Find the unique elements of an array, ignoring shape.

    Uses a hash table to find the unique elements if possible.
    """
    ar = np.asanyarray(ar).flatten()
    if len(ar.shape) != 1:
        # np.matrix, and maybe some other array subclasses, insist on keeping
        # two dimensions for all operations. Coerce to an ndarray in such cases.
        ar = np.asarray(ar).flatten()

    optional_indices = return_index or return_inverse

    if (optional_indices or return_counts) and not sorted:
        raise ValueError(
            "Currently, `sorted` can only be False if `return_index`, "
            "`return_inverse`, and `return_counts` are all False."
        )

    # masked arrays are not supported yet.
    if not optional_indices and not return_counts and not np.ma.is_masked(ar):
        # First we convert the array to a numpy array, later we wrap it back
        # in case it was a subclass of numpy.ndarray.
        conv = _array_converter(ar)
        ar_, = conv

        if (hash_unique := _unique_hash(ar_)) is not NotImplemented:
            if sorted:
                hash_unique.sort()
            # We wrap the result back in case it was a subclass of numpy.ndarray.
            return (conv.wrap(hash_unique),)

    # If we don't use the hash map, we use the slower sorting method.
    if optional_indices:
        perm = ar.argsort(kind='mergesort' if return_index else 'quicksort')
        aux = ar[perm]
    else:
        ar.sort()
        aux = ar
    mask = np.empty(aux.shape, dtype=np.bool)
    mask[:1] = True
    if (equal_nan and aux.shape[0] > 0 and aux.dtype.kind in "cfmM" and
            np.isnan(aux[-1])):
        if aux.dtype.kind == "c":  # for complex all NaNs are considered equivalent
            aux_firstnan = np.searchsorted(np.isnan(aux), True, side='left')
        else:
            aux_firstnan = np.searchsorted(aux, aux[-1], side='left')