{
  "category": "Lookup and reference functions",
  "display_name": "HLOOKUP",
  "entry_kind": "built_in_function",
  "eval_api": {
    "method": "POST",
    "url": "https://handbook.dnacalc.com/api/eval"
  },
  "function_id": "FUNC.HLOOKUP",
  "implementation_state": {
    "answer_bank": null,
    "detail": "Recorded: 30 workbook cells.",
    "dnacalc_commit": "9e1884369a678158dd82d3d74188daf986995e87",
    "in_catalogue": true,
    "state": "matches",
    "state_label": "Matches every recorded Excel answer",
    "summary": "Matches all 30 recorded answers",
    "workbook_corpus": {
      "cells": 31,
      "matched": 30,
      "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#L168-L179",
        "https://github.com/DnaCalc/DnaCalc/blob/9e1884369a678158dd82d3d74188daf986995e87/crates/calc-func/src/catalogue/lookup.rs#L1382-L1388"
      ],
      "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#L168-L179",
      "state": "matches",
      "suite": null,
      "summary": "Matches all 30 recorded answers"
    }
  ],
  "json_url": "https://handbook.dnacalc.com/api/functions/FUNC.HLOOKUP.json",
  "name": "HLOOKUP",
  "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`** is the modern replacement for both. It searches in either direction, defaults to\n  **exact** match, takes an explicit not-found argument instead of `#N/A`, and separates the\n  search array from the return array so `row_index_num` disappears entirely. New code should use\n  it; Microsoft retains `HLOOKUP` for workbook compatibility.\n- **`INDEX` + `MATCH`** is the classic decomposition: `MATCH` finds the position, `INDEX`\n  retrieves it. It removes the index-counting fragility and lets the search row sit anywhere in\n  the table rather than being forced to the top.\n- **`LOOKUP`'s array form** does something similar and is documented by Microsoft as provided\n  chiefly for compatibility with other spreadsheet programs; `HLOOKUP` is the better-specified\n  choice.\n- **`MATCH`** alone carries the same three-way match-type argument and the same sorted-data\n  requirement for its approximate modes, so the hazard described above is shared.\n\n\n",
    "how-to-implement": "\n### Notes for implementers\n\n- **The two lanes are two algorithms and must be tested as two functions.** Exact match is a\n  linear scan with wildcard support; approximate match is an ordered search. Sharing one code\n  path between them is where wildcard characters start leaking into the sorted lane, or where\n  the sorted lane's early exit silently truncates an exact scan.\n- **Do not validate sortedness.** Excel does not, and an implementation that errors on unsorted\n  input will reject workbooks that Excel computes. The obligation is to match the observable\n  answer, including on unsorted data — which means the search strategy itself is observable and\n  cannot be freely chosen.\n- **Wildcard escaping.** `~` before `?` or `*` makes the character literal. A naive\n  glob-to-regex translation that does not honour `~` will match the wrong cells and never error.\n- **Selective dereference.** The reference-aware preparation exists so that a lookup over a\n  large `table_array` need not materialize it. The shared model records this as function-local\n  and not generalized, so an implementation should treat it as a permitted optimization within\n  this function rather than as a pipeline guarantee.\n\n\n",
    "quirks": "\n### Result and edge cases\n\n\nReturn kind: whatever the located cell holds — number, text, logical, error, or a blank\nnormalized under the publication rules in\n[the value universe](../model/01-value-universe.md).\n\nEdge cases specific to this function:\n\n- **Duplicate keys.** On the exact lane the first match wins. On the approximate lane, which of\n  several equal keys is found depends on the search strategy, and the Handbook has not\n  established Excel's choice.\n- **Mixed types in the search row.** Excel's ordering across numbers, text and logicals is a\n  total order, but where the type boundaries fall governs what \"largest value less than or\n  equal\" means on a mixed row. Not established here.\n- **Text comparison** is case-insensitive in Excel's usual convention, and the collation used\n  for the approximate lane's ordering may be locale-dependent. Not established here.\n- **Empty cells in the search row** — skipped, matched by an empty lookup value, or treated as\n  a zero — is a real question for approximate match over a partially filled table.\n\n\n\n### Errors\n\n\nMicrosoft's page documents the error surface:\n\n| Condition | Result |\n|---|---|\n| No match found (either lane) | `#N/A` |\n| `lookup_value` smaller than every value in the top row, approximate lane | `#N/A` |\n| `row_index_num` less than 1 | `#VALUE!` |\n| `row_index_num` greater than the number of rows in `table_array` | `#REF!` |\n\nThe `#VALUE!` / `#REF!` split on `row_index_num` is worth keeping straight: too small is a bad\nargument, too large is a bad reference. That asymmetry is inherited from the C-API era and is\nshared with `VLOOKUP`.\n\nAn error value in `lookup_value` propagates under the universal rule in\n[coercion and lifting](../model/02-coercion-and-lifting.md). An error value sitting in the\n*located* cell is simply the result — it is a value like any other.\n\n\n",
    "sources": "- Microsoft, HLOOKUP function —\n  <https://support.microsoft.com/en-us/office/hlookup-function-a3034eec-b719-4ba3-bb65-e1ad662ed95f>\n  (the two match modes, the ascending-sort requirement for approximate match, wildcard support\n  on exact match, and the `#N/A` / `#VALUE!` / `#REF!` conditions).\n- Handbook `content/model/02-coercion-and-lifting.md` (error propagation; reference resolution).\n- Handbook `data/functions/FUNC.HLOOKUP.json` (arity, curated per-argument notes on\n  `row_index_num` truncation and the omitted-`range_lookup` default) and\n  `data/presence/FUNC.HLOOKUP.json` (implementing module shared with `VLOOKUP`).\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`HLOOKUP(lookup_value, table_array, row_index_num, [range_lookup])` searches the **top row** of\n`table_array` for `lookup_value`, and returns the value in the same column from the row given by\n`row_index_num`, counting the top row as row 1.\n\n\"Searches\" means one of two genuinely different algorithms, chosen by `range_lookup`:\n\n**Exact match (`range_lookup` = FALSE).** A linear scan of the top row for a value equal to\n`lookup_value`; the first match wins. Microsoft documents that in this mode wildcards are\nhonoured in a text `lookup_value`: `?` matches any single character, `*` any run of characters,\nand `~` escapes a literal `?` or `*`. If nothing matches, the result is `#N/A`.\n\n**Approximate match (`range_lookup` = TRUE, or omitted).** The top row is assumed **sorted in\nascending order**, and the function finds the largest value that is less than or equal to\n`lookup_value`. This is a range lookup in the literal sense — it is how tax bands, grade\nboundaries and shipping tiers are expressed. If `lookup_value` is smaller than every value in\nthe top row, the result is `#N/A`.\n\nThe assumption in the second mode is not checked, and this is the function's defining hazard.\nMicrosoft documents that the values must be sorted ascending; on unsorted data the function does\nnot error, it simply returns something. What it returns depends on the search strategy the\nimplementation uses, which is why \"approximate match on unsorted data\" is a bug that survives\nreview — the answer looks like an answer.\n\n\n\n### Arguments\n\n\n| Argument | Meaning |\n|---|---|\n| `lookup_value` | The value sought in the top row. May be a value, a reference, or text — and, on the exact-match lane, wildcard text. |\n| `table_array` | The table to search. The top row is the search axis; the returned value comes from a row of the same block. |\n| `row_index_num` | Which row of `table_array` supplies the result, counting the top row as 1. |\n| `range_lookup` | Logical. `TRUE` or omitted selects approximate match; `FALSE` selects exact match. |\n\nThe two positions that are routinely misunderstood:\n\n"
  },
  "signature": {
    "display": "HLOOKUP(lookup_value, table_array, row_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": "row_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 row-index validation.",
        "short": "A value from a given row of a table, in the column found by searching the top row",
        "short_source": "dna-calc-own"
      },
      "docs": {
        "english_description": "Looks in the top row of an array and returns the value of the indicated cell",
        "microsoft_url": "https://support.microsoft.com/en-us/office/hlookup-function-a3034eec-b719-4ba3-bb65-e1ad662ed95f"
      },
      "entry_kind": "built_in_function",
      "localized_family_names": null,
      "localized_names": [
        {
          "locale": "ar-SA",
          "name": "HLOOKUP"
        },
        {
          "locale": "bg-BG",
          "name": "HLOOKUP"
        },
        {
          "locale": "cs-CZ",
          "name": "VVYHLEDAT"
        },
        {
          "locale": "da-DK",
          "name": "VOPSLAG"
        },
        {
          "locale": "de-DE",
          "name": "WVERWEIS"
        },
        {
          "locale": "el-GR",
          "name": "HLOOKUP"
        },
        {
          "locale": "en-AU",
          "name": "HLOOKUP"
        },
        {
          "locale": "en-GB",
          "name": "HLOOKUP"
        },
        {
          "locale": "en-US",
          "name": "HLOOKUP"
        },
        {
          "locale": "es-ES",
          "name": "BUSCARH"
        },
        {
          "locale": "et-EE",
          "name": "HLOOKUP"
        },
        {
          "locale": "fi-FI",
          "name": "VHAKU"
        },
        {
          "locale": "fr-FR",
          "name": "RECHERCHEH"
        },
        {
          "locale": "he-IL",
          "name": "HLOOKUP"
        },
        {
          "locale": "hr-HR",
          "name": "HLOOKUP"
        },
        {
          "locale": "hu-HU",
          "name": "VKERES"
        },
        {
          "locale": "id-ID",
          "name": "HLOOKUP"
        },
        {
          "locale": "it-IT",
          "name": "CERCA.ORIZZ"
        },
        {
          "locale": "ja-JP",
          "name": "HLOOKUP"
        },
        {
          "locale": "ko-KR",
          "name": "HLOOKUP"
        },
        {
          "locale": "lt-LT",
          "name": "HLOOKUP"
        },
        {
          "locale": "lv-LV",
          "name": "HLOOKUP"
        },
        {
          "locale": "nb-NO",
          "name": "FINN.KOLONNE"
        },
        {
          "locale": "nl-NL",
          "name": "HORIZ.ZOEKEN"
        },
        {
          "locale": "pl-PL",
          "name": "WYSZUKAJ.POZIOMO"
        },
        {
          "locale": "pt-BR",
          "name": "PROCH"
        },
        {
          "locale": "pt-PT",
          "name": "PROCH"
        },
        {
          "locale": "ro-RO",
          "name": "HLOOKUP"
        },
        {
          "locale": "ru-RU",
          "name": "ГПР"
        },
        {
          "locale": "sk-SK",
          "name": "HLOOKUP"
        },
        {
          "locale": "sl-SI",
          "name": "HLOOKUP"
        },
        {
          "locale": "sr-Latn-RS",
          "name": "HLOOKUP"
        },
        {
          "locale": "sv-SE",
          "name": "LETAKOLUMN"
        },
        {
          "locale": "th-TH",
          "name": "HLOOKUP"
        },
        {
          "locale": "tr-TR",
          "name": "YATAYARA işlevi"
        },
        {
          "locale": "uk-UA",
          "name": "HLOOKUP"
        },
        {
          "locale": "vi-VN",
          "name": "HLOOKUP"
        },
        {
          "locale": "zh-CN",
          "name": "HLOOKUP"
        },
        {
          "locale": "zh-HK",
          "name": "HLOOKUP"
        },
        {
          "locale": "zh-TW",
          "name": "HLOOKUP"
        }
      ],
      "signature": {
        "display": "HLOOKUP(lookup_value, table_array, row_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": "row_index_num",
            "optional": false,
            "repeats": false
          },
          {
            "description": "Omitted means approximate-match mode.",
            "name": "range_lookup",
            "optional": true,
            "repeats": false
          }
        ],
        "trailing_repeats": false
      },
      "surface_name": "HLOOKUP",
      "version_marker": null,
      "xlcall": {
        "code": "101",
        "symbol": "xlfHlookup"
      }
    },
    "microsoft": "https://support.microsoft.com/en-us/office/hlookup-function-a3034eec-b719-4ba3-bb65-e1ad662ed95f"
  },
  "try_it": {
    "formula": "=HLOOKUP(2,{1,2;\"one\",\"two\"},2,FALSE)"
  },
  "url": "https://handbook.dnacalc.com/functions/HLOOKUP.html",
  "variants": [],
  "versions": []
}