{
  "category": "Lookup and reference functions",
  "display_name": "VLOOKUP",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.VLOOKUP",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Recorded: 63 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 63 recorded answers",
    "workbook_corpus": {
      "cells": 64,
      "matched": 63,
      "pending": 1
    }
  },
  "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#L156-L167",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/lookup.rs#L1367-L1380"
      ],
      "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::hits",
        "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::first_hit",
        "catalogue/lookup::bisect",
        "catalogue/lookup::index_number",
        "catalogue/lookup::table_lookup",
        "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#L156-L167",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 63 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.VLOOKUP.json",
  "name": "VLOOKUP",
  "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- **`XLOOKUP` supersedes `VLOOKUP` and `HLOOKUP`.** `XLOOKUP` separates the lookup vector from\n  the return vector (so no fragile column index), defaults to **exact** match, can search\n  backwards, offers an `if_not_found` value instead of `#N/A`, and can return to the left of\n  the search column. For new work, `XLOOKUP` is the function to reach for. Microsoft has not\n  deprecated `VLOOKUP` — it remains fully supported, and it remains what tens of millions of\n  existing workbooks contain.\n- **`LOOKUP`** is the older, weaker ancestor: vector and array forms, approximate match only.\n- Readers confuse `VLOOKUP` with `FILTER` (which returns *all* matching rows rather than one)\n  and with `XMATCH` (position, not value).\n\n\n",
    "how-to-implement": "\n### Notes for implementers\n\n- The two modes are different search procedures over the same data, not one procedure with a\n  comparison tweak. An implementation that treats approximate match as \"exact match, else\n  nearest\" will diverge on unsorted input, which is precisely the case real workbooks hit.\n- Blanks in the search column are skippable candidates in the approximate path, not aborts.\n  Getting this wrong turns a common trailing-blank range into a whole-formula `#N/A`.\n- `lookup_value` must be admitted as an array, with the result spilling elementwise. This is\n  easy to miss when the scalar lane is written first.\n\n\n",
    "quirks": "\n### The omitted fourth argument\n\n\nThis is the part of `VLOOKUP` worth stating at length, because the cost is asymmetric and\ntherefore invisible.\n\n**Omitting `range_lookup` selects approximate match.** `VLOOKUP(v, T, k)` and\n`VLOOKUP(v, T, k, TRUE)` are the same call. A reader who writes the three-argument form\nintending \"find `v` in the table\" has instead written \"find the largest key not exceeding `v`,\ntrusting that the first column is sorted ascending\".\n\nWhy this default is expensive is not that it is wrong — it is the right default for the\nbracket-lookup use it was designed for — but that **its failure mode is silent**:\n\n| Situation | Exact mode (`FALSE`) | Approximate mode (omitted / `TRUE`) |\n|---|---|---|\n| Key present | the matching row | the matching row |\n| Key absent | `#N/A` — visible | some other row's value — invisible |\n| Column not sorted | irrelevant | an arbitrary row's value — invisible |\n\nAn exact-match miss announces itself as `#N/A` and stops the spreadsheet. An approximate-match\nmiss returns a plausible number of the right type, in the right format, in the right cell. It\npropagates. It is discovered, if ever, by someone reconciling totals months later.\n\nThe unsorted case deserves separate emphasis. Because Microsoft documents only that\napproximate match on unsorted data \"may not return the correct value\", the behaviour on\nunsorted input is *undefined by the documentation* rather than defined-and-undesirable. What\nis observable is that it is not random: for a fixed table it is stable and repeatable, which is\nexactly what makes it survive testing. The shape of those wrong answers is consistent with a\nbisection over the search column, but the Handbook has not pinned the search procedure and\ndoes not assert one.\n\nTwo practical consequences the Handbook is willing to state plainly:\n\n1. If you mean exact lookup, pass `FALSE` (or `0`) explicitly, every time. There is no\n   defensible reason to rely on the default when the intent is exact.\n2. If you mean bracket lookup, pass `TRUE` explicitly and say so in a comment, so the next\n   reader knows the sorted-column precondition is load-bearing rather than accidental.\n\n\n\n### Result and edge cases\n\n\nThe return kind is whatever the selected cell holds — number, text, logical, error, or the\nzero that a referenced empty cell publishes. `VLOOKUP` performs no conversion on the value it\nreturns; it is a selector, not a coercer. The general rules for how empty cells, omitted\narguments and error values reach a function are in\n[the call pipeline](../model/03-call-pipeline.md) and\n[coercion and lifting](../model/02-coercion-and-lifting.md); only the function-specific parts\nare stated here.\n\n- **Repeated keys.** Exact mode returns the first match scanning downward. Approximate mode's\n  choice among equal keys is not something the Handbook has pinned.\n- **Mixed types in the search column.** Approximate mode needs a total order over values, so a\n  column mixing numbers, text and logicals forces Excel's cross-type ordering into view.\n  Microsoft states such an order on the `MATCH` page — ascending means\n  `-2, -1, 0, 1, 2, …, A-Z, FALSE, TRUE`, so all numbers precede all text, and text precedes\n  the logicals. Whether `VLOOKUP`'s approximate lane uses that same order is expected but not\n  documented on `VLOOKUP`'s own page, and the Handbook has not verified it.\n\n\n\n### Errors\n\n\nAs documented by Microsoft (see Sources):\n\n| Error | Documented condition |\n|---|---|\n| `#N/A` | Exact mode and no matching key; or approximate mode where `lookup_value` is smaller than every key in the first column |\n| `#REF!` | `col_index_num` exceeds the number of columns in `table_array` |\n| `#VALUE!` | `col_index_num` less than 1 |\n\nError values arriving in `lookup_value` propagate rather than being searched for, which is the\nuniversal coercion rule rather than anything special to `VLOOKUP`. The Handbook has not\nverified what an error value *inside the search column* does to either mode.\n\n\n",
    "sources": "- Microsoft, *VLOOKUP function* —\n  <https://support.microsoft.com/en-us/office/vlookup-function-0bbc8083-26fe-4963-8ab8-93a18ad188a1>\n  (signature, argument meanings, the approximate-match sort requirement, and the documented\n  error conditions). Retrieval for this page was blocked by the upstream host; the documented\n  behaviour above is stated as documented behaviour and should be re-checked against the page.\n- Microsoft, *XLOOKUP function* —\n  <https://support.microsoft.com/en-us/office/xlookup-function-b7fd680e-6d10-43e6-84f9-88eae8bf5929>\n  (the supersession relationship and `XLOOKUP`'s exact-match default).\n- Handbook `content/model/02-coercion-and-lifting.md` and `content/model/03-call-pipeline.md`\n  (error propagation, array lifting, reference-aware argument preparation).\n- Handbook `data/functions/FUNC.VLOOKUP.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": "- a lookup value `v`,\n- a table `T` with rows `1..r` and columns `1..c`,\n- a column index `k`,\n- and a mode flag,\n\n`VLOOKUP` searches **only the first column of `T`** — `T[1,1] … T[r,1]` — for a row index `i`,\nthen returns `T[i,k]`. Two things about that sentence carry most of the function's real-world\nfailure surface, and both are structural rather than numerical:\n\n1. **The search column is fixed at column 1 of `T`.** It is not a separate argument. The\n   caller chooses it implicitly by where the range starts, which is why inserting a column to\n   the left of a table changes what `VLOOKUP` searches.\n2. **`k` is an absolute index within `T`, not an offset from the search column.** `k = 1`\n   returns the search column itself; `k = 2` returns the column immediately to its right. `k`\n   is counted from `T`'s own left edge, so it is invalidated by any column inserted inside\n   `T` — the formula keeps working and silently returns a different column.\n\nThe choice of `i` depends on the mode flag, and the two modes are genuinely different\nalgorithms rather than two settings of one algorithm:\n\n- **Exact mode** (`range_lookup` = `FALSE`). `i` is the smallest row index whose first-column\n  value equals `v`. Equality is Excel's worksheet equality — case-insensitive for text, and,\n  for numbers, the tolerant near-equality Excel applies in comparison contexts rather than raw\n  IEEE-754 bit equality. If no row matches, the result is `#N/A`. The scan direction (top to\n  bottom, first match wins) is what makes the result deterministic when the key repeats.\n- **Approximate mode** (`range_lookup` = `TRUE`, or omitted). `i` is the row whose\n  first-column value is the **largest value not exceeding `v`**, under Excel's ordering of\n  values — and this answer is only meaningful if the first column is sorted in ascending\n  order. Microsoft documents the ascending-sort requirement as a caller obligation. It is a\n  precondition, not a checked constraint: Excel does not validate it and raises no error when\n  it is violated.\n\nApproximate mode is the older and, historically, the primary mode: it is what makes `VLOOKUP`\na *bracket lookup*, the tool for tax bands, shipping tiers, grade boundaries, and any table\nwhose keys are the lower bounds of intervals. Used that way it is exactly the right function.\nUsed as a substitute for exact lookup, it is the single most expensive default in the product.\n\n\n\n### Arguments\n\n\n**`lookup_value`** — the key to search for. Numbers, text and logicals are all admissible. In\nexact mode, `*` and `?` in a text lookup value are documented to act as wildcards (any run of\ncharacters, and any single character respectively), with `~` as the escape; that is a real\nbehavioural difference from a plain equality test, and it means a lookup key that legitimately\ncontains `*` or `?` must be escaped.\n\n**`table_array`** — the table. May be a reference or an array. Only its first column is\nsearched; the remaining columns exist solely as return material. A `table_array` whose left\nedge is not where the caller thinks it is produces wrong answers with no diagnostic.\n\n**`range_lookup`** — the mode flag. Optional. See below.\n\n\n"
  },
  "signature": {
    "display": "VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup])",
    "parameters": [
      {
        "description": "Supports exact and approximate lookup, including the current-baseline wildcard text lane.",
        "name": "lookup_value",
        "optional": false,
        "repeats": false
      },
      {
        "description": "Reference and array structure stays visible through the adapter seam.",
        "name": "table_array",
        "optional": false,
        "repeats": false
      },
      {
        "description": "Truncates toward zero before validation.",
        "name": "col_index_num",
        "optional": false,
        "repeats": false
      },
      {
        "description": "Omitted means approximate-match mode.",
        "name": "range_lookup",
        "optional": true,
        "repeats": false
      }
    ],
    "trailing_repeats": false
  },
  "sources": {
    "metadata": {
      "admission": {
        "label": "implemented",
        "reason": null
      },
      "arity": {
        "max": 4,
        "min": 3
      },
      "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": "WorkbookState",
        "kernel_signature_class": "LookupMatch",
        "rich_value_usage": "RichBlind",
        "surface_fec_dependency_profile": "RefOnly",
        "thread_safety_class": "SafePure",
        "volatility_class": "NonVolatile"
      },
      "descriptions": {
        "long": "Current-baseline the earlier implementation support covers exact and approximate numeric lookup, wildcard text lookup on the exact lane, logical lookup values, and column-index validation.",
        "short": "A value from a given column of a table, in the row found by searching the first column",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Looks in the first column of an array and moves across the row to return the value of a cell",
        "microsoft_url": "https://support.microsoft.com/en-us/office/vlookup-function-0bbc8083-26fe-4963-8ab8-93a18ad188a1"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "VLOOKUP"
        },
        {
          "locale": "bg-BG",
          "name": "VLOOKUP"
        },
        {
          "locale": "cs-CZ",
          "name": "SVYHLEDAT"
        },
        {
          "locale": "da-DK",
          "name": "LOPSLAG"
        },
        {
          "locale": "de-DE",
          "name": "SVERWEIS"
        },
        {
          "locale": "el-GR",
          "name": "VLOOKUP"
        },
        {
          "locale": "en-AU",
          "name": "VLOOKUP"
        },
        {
          "locale": "en-GB",
          "name": "VLOOKUP"
        },
        {
          "locale": "en-US",
          "name": "VLOOKUP"
        },
        {
          "locale": "es-ES",
          "name": "BUSCARV"
        },
        {
          "locale": "et-EE",
          "name": "VLOOKUP"
        },
        {
          "locale": "fi-FI",
          "name": "PHAKU"
        },
        {
          "locale": "fr-FR",
          "name": "RECHERCHEV"
        },
        {
          "locale": "he-IL",
          "name": "VLOOKUP"
        },
        {
          "locale": "hr-HR",
          "name": "VLOOKUP"
        },
        {
          "locale": "hu-HU",
          "name": "FKERES"
        },
        {
          "locale": "id-ID",
          "name": "VLOOKUP"
        },
        {
          "locale": "it-IT",
          "name": "CERCA.VERT"
        },
        {
          "locale": "ja-JP",
          "name": "VLOOKUP"
        },
        {
          "locale": "ko-KR",
          "name": "VLOOKUP"
        },
        {
          "locale": "lt-LT",
          "name": "VLOOKUP"
        },
        {
          "locale": "lv-LV",
          "name": "VLOOKUP"
        },
        {
          "locale": "nb-NO",
          "name": "FINN.RAD"
        },
        {
          "locale": "nl-NL",
          "name": "VERT.ZOEKEN"
        },
        {
          "locale": "pl-PL",
          "name": "WYSZUKAJ.PIONOWO"
        },
        {
          "locale": "pt-BR",
          "name": "PROCV"
        },
        {
          "locale": "pt-PT",
          "name": "PROCV"
        },
        {
          "locale": "ro-RO",
          "name": "VLOOKUP"
        },
        {
          "locale": "ru-RU",
          "name": "ВПР"
        },
        {
          "locale": "sk-SK",
          "name": "VLOOKUP"
        },
        {
          "locale": "sl-SI",
          "name": "VLOOKUP"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "VLOOKUP"
        },
        {
          "locale": "sv-SE",
          "name": "LETARAD"
        },
        {
          "locale": "th-TH",
          "name": "VLOOKUP"
        },
        {
          "locale": "tr-TR",
          "name": "DÜŞEYARA"
        },
        {
          "locale": "uk-UA",
          "name": "VLOOKUP"
        },
        {
          "locale": "vi-VN",
          "name": "Hàm VLOOKUP"
        },
        {
          "locale": "zh-CN",
          "name": "VLOOKUP"
        },
        {
          "locale": "zh-HK",
          "name": "VLOOKUP"
        },
        {
          "locale": "zh-TW",
          "name": "VLOOKUP"
        }
      ],
      "signature": {
        "display": "VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup])",
        "parameters": [
          {
            "description": "Supports exact and approximate lookup, including the current-baseline wildcard text lane.",
            "name": "lookup_value",
            "optional": false,
            "repeats": false
          },
          {
            "description": "Reference and array structure stays visible through the adapter seam.",
            "name": "table_array",
            "optional": false,
            "repeats": false
          },
          {
            "description": "Truncates toward zero before validation.",
            "name": "col_index_num",
            "optional": false,
            "repeats": false
          },
          {
            "description": "Omitted means approximate-match mode.",
            "name": "range_lookup",
            "optional": true,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "VLOOKUP",
      "version_marker": null,
      "xlcall": {
        "code": "102",
        "symbol": "xlfVlookup"
      }
    },
    "microsoft": "https://support.microsoft.com/en-us/office/vlookup-function-0bbc8083-26fe-4963-8ab8-93a18ad188a1"
  },
  "try_it": {
    "formula": "=VLOOKUP(2,{1,\"one\";2,\"two\"},2,FALSE)"
  },
  "url": "https://handbook.dnacalc.com/functions/VLOOKUP.html",
  "variants": [],
  "versions": []
}