public function sortBy($callback = null, $direction = 'ASC')
    {
        if (!is_callable($callback)) {
            if (is_null($callback) or is_string($callback) && 'values' === strtolower($callback)) {
                $callback = function ($value, $key) { return $value; };
            } else if (is_string($callback) && 'keys' === strtolower($callback)) {
                $callback = function ($value, $key) { return $key; };
            } else {
                throw new \InvalidArgumentException(sprintf(
                    'Dictionary::sort() argument must be "keys", "values" or callable, '
                    . 'but is "%s".', gettype($callback)
                ));
            }
        }

        if ($direction !== SORT_ASC && $direction !== SORT_DESC) {
            if (is_string($direction)) {
                switch (strtolower($direction)) {
                    case 'asc':
                        $direction = SORT_ASC;
                        break;
                    case 'desc':
                        $direction = SORT_DESC;
                        break;
                    default:
                        throw new \InvalidArgumentException(sprintf(
                            'Direction must be "asc" or "desc", but is "%s".', $direction));
                }
            } else {
                throw new \InvalidArgumentException(sprintf(
                    'Direction must be string "asc" or "desc", but is "%s".', gettype($direction)));
            }
        }

        $order = [];
        foreach ($this as $key => $value) {
            $order[] = $callback($value, $key);
        }

        array_multisort($order, $direction, SORT_REGULAR, $this->data, $this->keys);
        
        return $this; //to allow chaining
    }