{
  "category": "Lookup and reference functions",
  "display_name": "LOOKUP",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.LOOKUP",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Recorded: 38 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 38 recorded answers",
    "workbook_corpus": {
      "cells": 38,
      "matched": 38
    }
  },
  "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/lookup.rs#L180-L190",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/lookup.rs#L1446-L1460",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/lookup.rs#L1462-L1523"
      ],
      "repository": "https://github.com/DnaCalc/DnaCalc",
      "shared_code": [
        "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",
        "catalogue/lookup::eager",
        "catalogue/lookup::Item::rank",
        "catalogue/lookup::item",
        "catalogue/lookup::lookup_key",
        "catalogue/lookup::text_limit",
        "catalogue/lookup::equal",
        "catalogue/lookup::error_rank",
        "catalogue/lookup::order",
        "catalogue/lookup::pattern",
        "catalogue/lookup::matches",
        "catalogue/lookup::Line::len",
        "catalogue/lookup::Line::scan",
        "catalogue/lookup::Line::held",
        "catalogue/lookup::reader",
        "catalogue/lookup::cell_value",
        "catalogue/lookup::rows",
        "catalogue/lookup::columns",
        "catalogue/lookup::row_line",
        "catalogue/lookup::column_line",
        "catalogue/lookup::line",
        "catalogue/lookup::value",
        "catalogue/lookup::span",
        "catalogue/lookup::shape",
        "catalogue/lookup::bisect",
        "catalogue/lookup::index_number",
        "catalogue/lookup::index",
        "catalogue/lookup::selector",
        "catalogue/lookup::indexed",
        "catalogue/lookup::select",
        "catalogue/lookup::row",
        "catalogue/lookup::column",
        "catalogue/lookup::position_of",
        "catalogue/lookup::areas"
      ],
      "source": "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/lookup.rs#L180-L190",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 38 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.LOOKUP.json",
  "name": "LOOKUP",
  "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- **`XLOOKUP`** is the modern replacement and supersedes every use of `LOOKUP`: it defaults to\n  exact match, takes an explicit not-found value, searches in either direction, supports a\n  reverse search mode, and separates the search array from the return array — which is the\n  vector form's flexibility with the sizes checked. New code should use it. Microsoft retains\n  `LOOKUP` for workbook compatibility.\n- **`VLOOKUP` / `HLOOKUP`** are the better-specified relatives, and are what Microsoft's own\n  page recommends in place of `LOOKUP`'s array form. They also offer an exact-match mode, which\n  `LOOKUP` does not.\n- **`MATCH`** with `match_type` 1 implements the same \"largest not exceeding\" rule and returns\n  the position instead of the value; `INDEX`/`MATCH` is the decomposed, checkable version of\n  what `LOOKUP` does in one call.\n- **`XMATCH`** adds a binary-search mode that makes the sortedness assumption explicit rather\n  than implicit — arguably the honest version of `LOOKUP`'s contract.\n- **Module siblings.** The reference implementation places `LOOKUP` with `FREQUENCY`, `PROB` and\n  `MODE.MULT`. That grouping is about shared ordered-bin machinery, not about Excel's\n  categories — `FREQUENCY`'s bin assignment is the same \"largest not exceeding\" question asked\n  of a whole array at once.\n\n\n",
    "how-to-implement": "\n### Notes for implementers\n\n\n- **The search strategy is observable.** Because Excel does not validate sortedness, whatever\n  the implementation does on unsorted data *is* the specification. A binary search and a linear\n  scan agree on sorted input and disagree on unsorted input, so the choice is not free and\n  cannot be made on performance grounds alone. Matching Excel here requires evidence, not\n  reasoning.\n- **The array form's axis rule uses `>`, not `>=`.** Wide means more columns than rows; square\n  falls to the column branch. An implementation that writes the comparison the other way is\n  wrong only for square arrays, which is exactly the case a hand-written test is least likely to\n  include.\n- **Size mismatch between the vectors needs a declared policy**, since the documentation states\n  a requirement rather than a behaviour. Silently truncating, silently extending, and erroring\n  are all defensible and all different.\n- **Errors in the key vector need a declared policy too**, and it is not the ordinary\n  propagation rule if the well-known idiom works as reported.\n- **Reference-awareness is a performance property here.** Searching a whole-column key vector\n  should not materialize a million cells; the shared model records selective dereference as a\n  function-local capability rather than a general pipeline one.\n- **Do not \"improve\" `LOOKUP`.** Rejecting unsorted input, or checking vector sizes, would make\n  a better function and a worse `LOOKUP` — workbooks that Excel computes would stop computing.\n\n\n",
    "quirks": "\n### The two forms\n\n\nMicrosoft's documentation presents `LOOKUP` as two forms sharing a name, distinguished by\nwhether the second argument is a vector or a rectangular array.\n\n### Vector form — `LOOKUP(lookup_value, lookup_vector, [result_vector])`\n\n`lookup_vector` is a single row or a single column of keys, in ascending order. The search finds\nthe largest key not exceeding `lookup_value`, and the answer is the correspondingly positioned\nelement of `result_vector`. Microsoft documents that `lookup_vector` and `result_vector` must be\nthe same size; where they are not, the behaviour is not something to rely on.\n\nThe two vectors need not be parallel in orientation or adjacent in the sheet — they can be a row\nand a column, or live on different sheets. Alignment is by *position*, not by geometry, which is\nwhat makes the vector form more flexible than `HLOOKUP` and `VLOOKUP` and also what makes a\nsize mismatch so easy to introduce.\n\nOmitting `result_vector` returns the matched key itself, from `lookup_vector`.\n\n### Array form — `LOOKUP(lookup_value, array)`\n\nOne rectangular argument serves as both keys and results. Microsoft documents the axis rule: if\n`array` has more columns than rows, the search runs along the **first row** and the result comes\nfrom the **last row**; otherwise — square, or taller than wide — the search runs along the\n**first column** and the result comes from the **last column**.\n\nThat rule is shape-driven and therefore silent: an array that changes from wide to tall as data\nis added changes which axis is searched, with no error and no visible cause. Microsoft's own\ndocumentation says the array form is provided for compatibility with other spreadsheet programs\nand recommends `HLOOKUP` or `VLOOKUP` instead. The Handbook agrees, and would put it more\nstrongly: the array form's shape rule is a trap, and there is no case where it is the clearest\nway to express an intention.\n\n\n\n### Result and edge cases\n\n\nReturn kind: whatever the aligned element holds — number, text, logical, or error.\n\n`LOOKUP` is reference-aware (`arg_preparation_profile: RefsVisibleInAdapter`), so its vectors\narrive as live references and need not be materialized whole to be searched.\n\n- **`lookup_value` below every key** — `#N/A`. This is the documented miss, and it is the *only*\n  miss: a value above every key matches the last one, because the rule is \"largest key not\n  exceeding\".\n- **Duplicate keys** — which of several equal keys is selected is a consequence of the search\n  strategy, and is not established here.\n- **Mixed types among the keys.** \"Largest not exceeding\" needs a total order across numbers,\n  text and logicals. Excel has one; where its type boundaries fall, and what that means for a\n  mixed key vector, is not established here.\n- **Empty cells among the keys** — skipped, treated as a key, or treated as zero — is a real\n  question for partially filled vectors, and the answer interacts with the sortedness\n  assumption.\n- **Errors among the keys.** This case deserves its own paragraph, below.\n- **Text keys** compare case-insensitively under Excel's usual convention, and the collation\n  governing their order may be locale-dependent. Not established here.\n\n**The `LOOKUP(2, 1/(condition), result)` idiom.** A widely used construction relies on `LOOKUP`\nsearching a vector that is deliberately full of `#DIV/0!` errors with a search value that\nexceeds every non-error entry, to retrieve the *last* row satisfying a condition. Its mechanism\nrequires that errors be passed over rather than propagated, and that an out-of-range search\nvalue settle on the last valid key even though the vector is not sorted. Microsoft's\ndocumentation does not describe this behaviour, and the Handbook states plainly: this idiom is\nfolklore that works, its mechanism is not documented, and nothing in the Handbook has verified\nit. It is the single most interesting open question on this page, because the idiom is load\nbearing in real workbooks and rests on unspecified behaviour.\n\n\n\n### Errors\n\n\nMicrosoft's page documents `#N/A` when `lookup_value` is smaller than the smallest key.\n\nBeyond that the documented error surface is thin, and thin for a reason: `LOOKUP`'s failure mode\nis usually not an error at all. Unsorted keys, mismatched vector sizes, and the array form's\nshape rule all produce *values*. A function that returns the wrong number quietly is worse than\none that errors, and this is the function's defining characteristic.\n\nAn error value supplied as `lookup_value` propagates under the universal rule in\n[coercion and lifting](../model/02-coercion-and-lifting.md). Errors *inside* the key vector are\nthe open question described above.\n\n\n",
    "sources": "- Microsoft, LOOKUP function —\n  <https://support.microsoft.com/en-us/office/lookup-function-446d94af-663b-451d-8251-369d5e3864cb>\n  (the vector and array forms, the ascending-order requirement, the same-size requirement for\n  the two vectors, the array form's shape rule, the `#N/A` condition when the search value is\n  below every key, and the recommendation to prefer `HLOOKUP`/`VLOOKUP` over the array form).\n- Handbook `content/model/02-coercion-and-lifting.md` (error propagation; reference resolution).\n- Handbook `content/model/03-call-pipeline.md` (`RefsVisibleInAdapter`; selective dereference as\n  a function-local capability).\n- Handbook `content/model/06-claim-language.md` (why an undocumented but widely relied-upon\n  behaviour is published as an open question rather than as a fact).\n\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`LOOKUP` finds, among a sequence of keys, **the largest key that is less than or equal to the\nvalue you are looking for**, and returns the value aligned with it. It has no exact-match mode\nand no not-found argument: approximate is the only behaviour, and `#N/A` is the only miss.\n\nMicrosoft documents the requirement that makes it work: the keys must be in **ascending order**.\n`LOOKUP` does not verify this. On unsorted data it returns a value — some value — without\ncomplaint. Everything difficult about this function traces back to that one unchecked\nprecondition.\n\nThe pinned catalogue displays the vector form, `LOOKUP(lookup_value, lookup_vector, [result_vector])`, and accepts two or three arguments. The array form below shares that name but assigns a different role to its second argument.\n\n\n\n### Arguments\n\n\n| Argument | Meaning |\n|---|---|\n| `lookup_value` | The value searched for. May be a number, text, a logical, a name, or a reference to one. |\n| `lookup_vector` / `array` | The keys. A single row or column in the vector form; a rectangle in the array form, where the shape rule decides the axis. |\n| `result_vector` | Vector form only. The values to return, positionally aligned with `lookup_vector` and — per the documentation — the same size. Optional; omitted returns the key. |\n\nThe arity is 2 to 3, which is what makes the two forms indistinguishable from the signature\nalone: a two-argument call is the array form or a vector form without `result_vector`, and only\nthe second argument's shape tells you which.\n\nThe position readers misjudge most is `result_vector`'s **alignment**. It is positional, not\ngeometric: the *n*th element of `result_vector` pairs with the *n*th element of\n`lookup_vector`, in each vector's own reading order. A row paired with a column works; a\nfive-element key vector paired with a six-element result vector does not fail loudly.\n\n\n"
  },
  "signature": {
    "display": "LOOKUP(lookup_value, lookup_vector, [result_vector])",
    "parameters": [
      {
        "description": null,
        "name": "lookup_value",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "lookup_vector",
        "optional": false,
        "repeats": false
      },
      {
        "description": null,
        "name": "result_vector",
        "optional": true,
        "repeats": false
      }
    ],
    "trailing_repeats": false
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 3,
        "min": 2
      },
      "category": "Lookup and reference functions",
      "classification": {
        "arg_preparation_profile": "RefsVisibleInAdapter",
        "coercion_lift_profile": "Custom",
        "determinism_class": "Deterministic",
        "error_collapse_profile": "None",
        "fec_dependency_profile": "RefOnly",
        "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": "A value found by searching one row or column, taken from the matching place in another",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Looks up values in a vector or array",
        "microsoft_url": "https://support.microsoft.com/en-us/office/lookup-function-446d94af-663b-451d-8251-369d5e3864cb"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "LOOKUP"
        },
        {
          "locale": "bg-BG",
          "name": "LOOKUP"
        },
        {
          "locale": "cs-CZ",
          "name": "VYHLEDAT"
        },
        {
          "locale": "da-DK",
          "name": "SLÅ.OP"
        },
        {
          "locale": "de-DE",
          "name": "VERWEIS"
        },
        {
          "locale": "el-GR",
          "name": "LOOKUP"
        },
        {
          "locale": "en-AU",
          "name": "LOOKUP"
        },
        {
          "locale": "en-GB",
          "name": "LOOKUP"
        },
        {
          "locale": "en-US",
          "name": "LOOKUP"
        },
        {
          "locale": "es-ES",
          "name": "BUSCAR"
        },
        {
          "locale": "et-EE",
          "name": "LOOKUP"
        },
        {
          "locale": "fi-FI",
          "name": "HAKU"
        },
        {
          "locale": "fr-FR",
          "name": "RECHERCHE"
        },
        {
          "locale": "he-IL",
          "name": "LOOKUP"
        },
        {
          "locale": "hr-HR",
          "name": "LOOKUP"
        },
        {
          "locale": "hu-HU",
          "name": "KERES"
        },
        {
          "locale": "id-ID",
          "name": "LOOKUP"
        },
        {
          "locale": "it-IT",
          "name": "CERCA"
        },
        {
          "locale": "ja-JP",
          "name": "LOOKUP"
        },
        {
          "locale": "ko-KR",
          "name": "LOOKUP"
        },
        {
          "locale": "lt-LT",
          "name": "LOOKUP"
        },
        {
          "locale": "lv-LV",
          "name": "LOOKUP"
        },
        {
          "locale": "nb-NO",
          "name": "SLÅ.OPP"
        },
        {
          "locale": "nl-NL",
          "name": "OPZOEKEN"
        },
        {
          "locale": "pl-PL",
          "name": "WYSZUKAJ"
        },
        {
          "locale": "pt-BR",
          "name": "PROC"
        },
        {
          "locale": "pt-PT",
          "name": "PROC"
        },
        {
          "locale": "ro-RO",
          "name": "LOOKUP"
        },
        {
          "locale": "ru-RU",
          "name": "ПРОСМОТР"
        },
        {
          "locale": "sk-SK",
          "name": "LOOKUP"
        },
        {
          "locale": "sl-SI",
          "name": "LOOKUP"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "PRONALAŽENJE"
        },
        {
          "locale": "sv-SE",
          "name": "LETAUPP"
        },
        {
          "locale": "th-TH",
          "name": "LOOKUP"
        },
        {
          "locale": "tr-TR",
          "name": "ARA"
        },
        {
          "locale": "uk-UA",
          "name": "LOOKUP"
        },
        {
          "locale": "vi-VN",
          "name": "LOOKUP"
        },
        {
          "locale": "zh-CN",
          "name": "LOOKUP"
        },
        {
          "locale": "zh-HK",
          "name": "LOOKUP"
        },
        {
          "locale": "zh-TW",
          "name": "LOOKUP"
        }
      ],
      "signature": {
        "display": "LOOKUP(lookup_value, lookup_vector, [result_vector])",
        "parameters": [
          {
            "description": null,
            "name": "lookup_value",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "lookup_vector",
            "optional": false,
            "repeats": false
          },
          {
            "description": null,
            "name": "result_vector",
            "optional": true,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "LOOKUP",
      "version_marker": null,
      "xlcall": {
        "code": "28",
        "symbol": "xlfLookup"
      }
    },
    "microsoft": "https://support.microsoft.com/en-us/office/lookup-function-446d94af-663b-451d-8251-369d5e3864cb"
  },
  "try_it": {
    "formula": "=LOOKUP(2,{1;2;3},{10;20;30})"
  },
  "url": "https://handbook.dnacalc.com/functions/LOOKUP.html",
  "variants": [],
  "versions": []
}