{
  "category": "Lookup and reference functions",
  "display_name": "SORTBY",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.SORTBY",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Recorded: 14 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 14 recorded answers",
    "workbook_corpus": {
      "cells": 14,
      "matched": 14
    }
  },
  "implementations": [
    {
      "commit": "9e1884369a678158dd82d3d74188daf986995e87",
      "flavour": "excel-compatibility-candidate",
      "language": "Rust",
      "name": "CalcFunc",
      "own_code": [
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/arrays.rs#L640-L642",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/arrays.rs#L428-L482"
      ],
      "repository": "https://github.com/DnaCalc/DnaCalc",
      "shared_code": [
        "catalogue/arrays::prepared",
        "catalogue/arrays::prepared_one",
        "catalogue/arrays::as_array",
        "catalogue/arrays::integer",
        "catalogue/arrays::descending",
        "catalogue/arrays::kind",
        "catalogue/arrays::order",
        "catalogue/arrays::sorted_indices",
        "catalogue/arrays::reordered",
        "catalogue/comparison::numbers",
        "catalogue/comparison::in_band",
        "catalogue/comparison::Rounded::up",
        "catalogue/comparison::Roundings::of",
        "catalogue/comparison::Roundings::disjoint",
        "catalogue/comparison::texts",
        "catalogue/comparison/collation::compare",
        "catalogue/comparison/collation::weighed",
        "catalogue/comparison/collation::measured_iteration_extension",
        "catalogue/comparison/sort_key::table",
        "catalogue/comparison/sort_key::decode64",
        "catalogue/comparison/sort_key::Reader::byte",
        "catalogue/comparison/sort_key::Reader::varint",
        "catalogue/comparison/sort_key::Reader::done",
        "catalogue/comparison/sort_key::in_ranges",
        "catalogue/comparison/sort_key::build",
        "catalogue/comparison/sort_key::han_base",
        "catalogue/comparison/sort_key::derive",
        "catalogue/comparison/sort_key::shared_jamo_tails",
        "catalogue/comparison/sort_key::ordinary_jamo_prefix",
        "catalogue/comparison/sort_key::independent_jamo_suffix",
        "catalogue/comparison/sort_key::jamo_leading_primary",
        "catalogue/comparison/sort_key::leading_order",
        "catalogue/comparison/sort_key::Before::separate_special",
        "catalogue/comparison/sort_key::Gathered::kana",
        "catalogue/comparison/sort_key::Gathered::unit",
        "catalogue/comparison/sort_key::parts",
        "catalogue/comparison/sort_key::diacritic_order",
        "catalogue/comparison/sort_key::diacritics",
        "decimal::low32",
        "decimal::Natural::from_u64",
        "decimal::Natural::pow10",
        "decimal::Natural::normalize",
        "decimal::Natural::is_zero",
        "decimal::Natural::mul_add_small",
        "decimal::Natural::times_pow10",
        "decimal::Natural::times_pow5",
        "decimal::Natural::times_pow2",
        "decimal::ExactDecimal::binary",
        "ported/dynamic_array_reshape_family::coerce_prepared_to_number"
      ],
      "source": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/arrays.rs#L640-L642",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 14 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.SORTBY.json",
  "name": "SORTBY",
  "notes": [],
  "schema": "efh.function-reference/v1",
  "section_order": [
    "what-it-does",
    "try-it",
    "quirks",
    "how-to-implement",
    "version-differences",
    "alternatives"
  ],
  "sections": {
    "alternatives": "\n### Relationships\n\n\n- **`SORT`** is the single-key, internal-key sibling. `SORT(A, k, d)` is\n  `SORTBY(A, INDEX(A, 0, k), d)` in spirit; `SORTBY` is the more general of the two and is the\n  one to reach for whenever the key is computed or the sort is multi-level.\n- **`UNIQUE`** deduplicates; `SORTBY` never does.\n- **`FILTER`** selects; `SORTBY` reorders. `SORTBY(FILTER(...), FILTER(...))` requires the two\n  filters to agree, which is a common source of misalignment.\n- **`RANK` / `LARGE` / `SMALL`** answer order questions without materializing a permutation.\n- Readers confuse `SORTBY` with `SORT`'s `sort_index`, and expect a `by_col` argument that does\n  not exist.\n\n\n",
    "how-to-implement": "\n### Notes for implementers\n\n\n- The multi-key comparison is lexicographic with per-component direction. Implementing it as\n  repeated single-key sorts only works if the underlying sort is stable *and* the keys are\n  applied in reverse order; if stability is undecided, that trick is unsound.\n- The pairing structure means argument parsing is positional and irregular: an odd trailing\n  argument is a key, an even one is a direction. Validate `sort_order` values eagerly —\n  Microsoft documents `#VALUE!` for anything other than `1` or `-1`, so silently accepting `0`\n  or `2` is a divergence.\n- Shape validation belongs before sorting: rectangular key arrays and length mismatches are\n  documented as invalid, not as something to broadcast.\n- The omitted-slot default path is a recorded defect class in this family.\n- The cross-kind comparison order and the collation used for text should be the same ones\n  `SORT` uses; two orderings in one engine is a bug waiting for a witness.\n\n\n",
    "quirks": "\n### Result and edge cases\n\n\nThe return kind is an array of the same shape as `array`, spilled.\n\n- **Orientation.** Whether `SORTBY` reorders rows or columns follows from the orientation of\n  the key vectors — a column key sorts rows, a row key sorts columns. There is no `by_col`\n  flag; the shape carries the intent.\n- **Ties beyond the last key.** When every supplied key ties, the resulting order is whatever\n  the underlying sort does with equal elements. Stability is not documented, and the Handbook\n  has not pinned it.\n- **Mixed value kinds in a key vector** require the same cross-kind total order that `SORT`\n  needs, and it is equally unverified here.\n- **Text keys are collated**, and collation is locale-dependent.\n- Dynamic-array publication (`#SPILL!` when the result cannot be placed) is host-side\n  adaptation, described in [the call pipeline](../model/03-call-pipeline.md), not a `SORTBY`\n  outcome.\n\n\n\n### Errors\n\n\nDocumented by Microsoft:\n\n| Error | Documented condition |\n|---|---|\n| `#VALUE!` | A `sort_order` argument that is not `1` or `-1` |\n| `#REF!` | A linked source workbook is closed when the dynamic array refreshes |\n\n`#SPILL!` arises, as for every dynamic-array function, when the result has nowhere to land.\nMismatched sizes between `array` and a `by_array`, or a rectangular `by_array`, violate the\ndocumented shape rules; the documentation states the requirement without naming the error, and\nthe Handbook has not verified which error results.\n\n\n",
    "sources": "- Microsoft, *SORTBY function* —\n  <https://support.microsoft.com/en-us/office/sortby-function-cd2d7a62-1b93-435c-b561-d6a35134f28f>\n  (syntax, the key/order pair structure, the one-row-or-one-column and equal-size requirements,\n  the `#VALUE!` condition for a `sort_order` other than `1` or `-1`, and the cross-workbook\n  `#REF!` note). Retrieved for this page.\n- Handbook `content/model/01-value-universe.md` and `content/model/03-call-pipeline.md`.\n- Handbook `data/functions/FUNC.SORTBY.json` (signature, arity, classification axes).\n\n",
    "version-differences": "No comparison between specific Excel builds is recorded here yet. This does not establish that all builds behave alike.",
    "what-it-does": "\n`SORTBY` reorders an array using keys held **outside** it.\n\nGiven data `A` and key vectors `K₁, K₂, …` all of the same length as `A`'s sorted axis, and a\ndirection `d₁, d₂, …` for each, `SORTBY` computes the permutation `π` that orders the tuple\nsequence `(K₁[i], K₂[i], …)` lexicographically — `K₁` first, `K₂` breaking `K₁`'s ties, and so\non — each component ordered in its own direction. It then applies `π` to `A`.\n\nTwo properties distinguish it from `SORT`:\n\n1. **The keys need not be in the data.** You can sort a visible table by a hidden column, by a\n   computed expression (`SORTBY(names, LEN(names))`), or by a lookup result. Nothing of the key\n   appears in the output.\n2. **It is multi-level.** `SORT` sorts by one key. `SORTBY` composes an arbitrary number of\n   keys with independent directions, which is what \"sort by region ascending, then revenue\n   descending\" requires.\n\nAs with `SORT`, the result is a **permutation of the same shape**: nothing is deduplicated,\nnothing is filtered, and every element of `A` appears exactly once.\n\n\n\n### Arguments\n\n\nMicrosoft documents\n`SORTBY(array, by_array1, [sort_order1], [by_array2, sort_order2], …)`.\n\n| Argument | Required | Default | Meaning |\n|---|---|---|---|\n| `array` | yes | — | The range or array to sort |\n| `by_array1` | yes | — | The first key vector |\n| `sort_order1` | no | `1` | `1` ascending, `-1` descending |\n| `by_array2`, `sort_order2`, … | no | `1` | Further key/direction pairs, applied as tie-breakers |\n\nThe documented shape rules are strict and worth stating exactly, because they are the usual\nsource of `#VALUE!`:\n\n- each `by_array` must be **one row high or one column wide** — a key vector, never a\n  rectangle;\n- all the arguments must be the same size as each other, and `array`'s sorted dimension must\n  match the key vectors' length.\n\nThe argument positions most often misread are the pairs themselves. Because `sort_order` is\noptional *within* each pair, the arguments after the second are position-sensitive in a way\nthat is easy to get wrong: `SORTBY(A, K1, K2)` does not mean \"two keys\" — it means \"key `K1`,\nsort order `K2`\", and unless `K2` happens to be `1` or `-1` that is a `#VALUE!`. Two keys\nrequire the explicit order for the first: `SORTBY(A, K1, 1, K2, 1)`.\n\n"
  },
  "signature": {
    "display": "SORTBY(array, by_array1, [sort_order1], ...)",
    "parameters": [
      {
        "description": null,
        "name": "array",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "by_array1",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "sort_order1",
        "optional": true,
        "repeats": true
      }
    ],
    "trailing_repeats": true
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 255,
        "min": 2
      },
      "category": "Lookup and reference functions",
      "classification": {
        "arg_preparation_profile": "ValuesOnlyPreAdapter",
        "coercion_lift_profile": "Custom",
        "determinism_class": "Deterministic",
        "error_collapse_profile": "None",
        "fec_dependency_profile": "None",
        "host_interaction_class": "None",
        "kernel_signature_class": "Custom",
        "rich_value_usage": "RichBlind",
        "surface_fec_dependency_profile": "RefOnly",
        "thread_safety_class": "SafePure",
        "volatility_class": "NonVolatile"
      },
      "descriptions": {
        "long": null,
        "short": "An array sorted by the values of other arrays",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Sorts the contents of a range or array based on the values in a corresponding range or array",
        "microsoft_url": "https://support.microsoft.com/en-us/office/sortby-function-cd2d7a62-1b93-435c-b561-d6a35134f28f"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "SORTBY"
        },
        {
          "locale": "bg-BG",
          "name": "SORTBY"
        },
        {
          "locale": "cs-CZ",
          "name": "SORTBY"
        },
        {
          "locale": "da-DK",
          "name": "SORTER.EFTER"
        },
        {
          "locale": "de-DE",
          "name": "SORTBY"
        },
        {
          "locale": "el-GR",
          "name": "SORTBY"
        },
        {
          "locale": "en-AU",
          "name": "SORTBY"
        },
        {
          "locale": "en-GB",
          "name": "SORTBY"
        },
        {
          "locale": "en-US",
          "name": "SORTBY"
        },
        {
          "locale": "es-ES",
          "name": "ORDENARPOR"
        },
        {
          "locale": "et-EE",
          "name": "SORTBY"
        },
        {
          "locale": "fi-FI",
          "name": "LAJITTELE.ARVOJEN.PERUSTEELLA"
        },
        {
          "locale": "fr-FR",
          "name": "TRIERPAR*"
        },
        {
          "locale": "he-IL",
          "name": "SORTBY"
        },
        {
          "locale": "hr-HR",
          "name": "SORTBY"
        },
        {
          "locale": "hu-HU",
          "name": "RENDEZÉS.ALAP.SZERINT"
        },
        {
          "locale": "id-ID",
          "name": "SORTBY"
        },
        {
          "locale": "it-IT",
          "name": "SORTBY"
        },
        {
          "locale": "ja-JP",
          "name": "SORTBY"
        },
        {
          "locale": "ko-KR",
          "name": "SORTBY"
        },
        {
          "locale": "lt-LT",
          "name": "SORTBY"
        },
        {
          "locale": "lv-LV",
          "name": "SORTBY"
        },
        {
          "locale": "nb-NO",
          "name": "SORTBY"
        },
        {
          "locale": "nl-NL",
          "name": "SORTEREN.OP"
        },
        {
          "locale": "pl-PL",
          "name": "SORTUJ.WEDŁUG"
        },
        {
          "locale": "pt-BR",
          "name": "SORTBY"
        },
        {
          "locale": "pt-PT",
          "name": "ORDENARPOR"
        },
        {
          "locale": "ro-RO",
          "name": "SORTBY"
        },
        {
          "locale": "ru-RU",
          "name": "СОРТПО"
        },
        {
          "locale": "sk-SK",
          "name": "SORTBY"
        },
        {
          "locale": "sl-SI",
          "name": "SORTBY"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "SORTBY"
        },
        {
          "locale": "sv-SE",
          "name": "SORTERAEFTER"
        },
        {
          "locale": "th-TH",
          "name": "SORTBY"
        },
        {
          "locale": "tr-TR",
          "name": "SIRALAÖLÇÜT"
        },
        {
          "locale": "uk-UA",
          "name": "SORTBY"
        },
        {
          "locale": "vi-VN",
          "name": "SORTBY"
        },
        {
          "locale": "zh-CN",
          "name": "SORTBY"
        },
        {
          "locale": "zh-HK",
          "name": "SORTBY"
        },
        {
          "locale": "zh-TW",
          "name": "SORTBY"
        }
      ],
      "signature": {
        "display": "SORTBY(array, by_array1, [sort_order1], ...)",
        "parameters": [
          {
            "description": null,
            "name": "array",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "by_array1",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "sort_order1",
            "optional": true,
            "repeats": true
          }
        ],
        "trailing_repeats": true
      },
      "surface_name": "SORTBY",
      "version_marker": null,
      "xlcall": null
    },
    "microsoft": "https://support.microsoft.com/en-us/office/sortby-function-cd2d7a62-1b93-435c-b561-d6a35134f28f"
  },
  "try_it": {
    "formula": "=SORTBY({\"a\";\"b\";\"c\"},{3;1;2})"
  },
  "url": "https://handbook.dnacalc.com/functions/SORTBY.html",
  "variants": [],
  "versions": []
}