|
High-speed Lookups with Clusters: FINDROW(), FINDVALUE(), and FINDVALUE$() |
Each function below opens with the idea; click a Show me line for its syntax and examples, or Expand all.
Clusters provide a powerful way to create high-speed lookup tables,
ideal for tasks such as translation. Each field within a cluster can
serve as a searchable key, and lookups are performed using the
findrow() function.
findrow() supports all data types, allows duplicate keys,
and permits key values to change over time. Missing or unset values are
handled naturally using empty strings or zero—without special
cases, performance penalties, or data loss from duplicate empty keys.
findrow() is a highly optimized function that uses hashing
to perform lookups in O(1) constant time, enabling over
10 million searches per second on a modern PC, regardless of the
number of rows, subject to available RAM. This makes findrow() ideal for workloads
requiring fast, predictable data access.
Given a cluster name, a cluster field to search, and a value to match,
findrow() returns the first row where the key is found and
makes that row current, or returns 0 if the key is not
found.
To handle duplicate keys, findrow() supports an optional
parameter that selects the Nth occurrence of a matching key.
exact: true — findrow(cities->city$, 'Cebu', exact: true)
— with no need to spell the occurrence number first. (The older spelling, TRUE
as the fourth argument after an explicit nth, still works.)Program Explanation:
1. Cluster Definition:
A cluster named trans is defined to store English words and their corresponding German translations. The cluster has two fields: english$ for English words and german$ for German words.
2. Adding Data:
The program adds entries to the trans cluster:
'apple' is paired with the German word 'der Apfel'.'banana' is paired with the German word 'die Banane'.3. User Input Assignment:
The variable word$ is assigned the value of a$, which is assumed to be a word entered by the user that they want to translate.
4. Searching for English Word:
The program searches the trans cluster for a row where the english$ field matches word$ (the word entered by the user). The row number is stored in the variable row.
5. English to German Translation:
If the word is found (row > 0), the program prints the English word followed by its German translation, using ' >> ' as the separator. The loop then exits since the translation has been found.
6. Searching for German Word:
If the word wasn't found in the English field, the program then searches the trans cluster for a row where the german$ field matches word$.
7. German to English Translation:
If the word is found in the German field (row > 0), the program prints the German word followed by its English translation, again using ' >> ' as the separator. The loop then exits since the translation has been found.
8. No Translation Found:
If the word is not found in either the English or German fields, the program prints a message indicating that there is no translation available for the word entered by the user.
Sometimes one field cannot identify the row you want. An order belongs to a customer and covers a product. A game item belongs to a session and has a name. In every case like this, the row you are after is "the one where both fields match."
Give findrow() a second field/value pair, and it finds
exactly that row:
Use this whenever a row is identified by a combination — owner and name, date and department, session and resource. If you have ever glued two values together into a lookup field, you no longer need to:
Everything you know about findrow() still applies: matching
is case-insensitive unless you say otherwise, the matched row becomes the
current row, and findvalue() accepts the same extra pairs
when you want a value back instead of a row number.
customer$ + "|" + product$),
storing it on every row, rebuilding it on every change, and hoping no
value ever contained the separator character. Forget one rebuild —
or meet one value with a "|" in it — and lookups
silently return the wrong row.findrow() or findvalue(). The row must match
every pair. No combined key field exists, so there is nothing to
maintain and no separator to collide with.
The findvalue() and findvalue$() functions
look up a row in a cluster by searching one field, then return a
specified value when that row is found.
The returned value may come from the matched row, such as
emp->name$ or emp->salary, or it may be any
compatible expression, variable, or constant.
As a side effect, _integer is set to the matching row
number. If no matching row is found, _integer is set to
0.
The current cluster row is not changed by findvalue() or
findvalue$(). This means they can be safely used inside
cluster iteration loops without disturbing the loop's current row.
Problem: Business programs often need to find a row by one
field, then read another field from that same row. Without a helper
function, this requires a findrow(), a test for zero,
and a separate field reference.
Solution: findvalue() and
findvalue$() combine the lookup, the not-found test,
and the returned value into one expression.
Efficiency: The function uses Sheerpower's cluster lookup model directly. Any cluster field can become a key when searched, so no separate key/value table or declaration is needed.
Takeaway: Use findvalue() or
findvalue$() when the real intent is: find this row,
return this value, otherwise use this explicit fallback.
findvalue() searches a cluster field for a matching
value. If a matching row is found, Sheerpower positions the cluster
to that row, evaluates found_result, and returns the
resulting numeric value.
If no matching row is found, findvalue() returns
not_found_result.
Use findvalue() when the value being returned is numeric.
search_field
A cluster field designator that identifies the field to search. This
is the same form accepted by findrow(), such as
emp->id, emp->salary, or
invoice->number.
search_value
The value to match in search_field. The search value may
be numeric or string, depending on the field being searched.
found_result
The numeric value to return when a matching row is found. This may be
a numeric field designator, a numeric variable, or a numeric constant.
If found_result is a cluster field designator, it is
evaluated after the cluster has been positioned to the matched row.
not_found_result
The numeric value to return when no matching row is found.
This argument is required. There is no silent default.
nth
Optional. Selects which matching row to use when duplicate matches
exist. 1 means the first match and is the default.
exact: true
Optional, and named: makes string matching case-sensitive. Written by
name after the positional arguments, it needs no nth before
it (findvalue(..., exact: true)); the older positional
spelling, TRUE after an explicit nth, still
works.
findvalue() returns a numeric REAL value.
Both found_result and not_found_result must
be numeric.
findvalue$() searches a cluster field for a matching
value. If a matching row is found, Sheerpower positions the cluster
to that row, evaluates found_result, and returns the
resulting string value.
If no matching row is found, findvalue$() returns
not_found_result.
Use findvalue$() when the value being returned is a
string.
search_field
A cluster field designator that identifies the field to search. This
may be a string field, such as emp->name$, or a numeric
field, such as emp->id.
search_value
The value to match in search_field. The search value is
not determined by the $ on findvalue$().
The $ controls the return type, not the search type.
found_result
The string value to return when a matching row is found. This may be
a string field designator, a string variable, or a string constant.
If found_result is a cluster field designator, it is
evaluated after the cluster has been positioned to the matched row.
not_found_result
The string value to return when no matching row is found.
This argument is required. There is no silent default.
nth
Optional. Selects which matching row to use when duplicate matches
exist. 1 means the first match and is the default.
exact: true
Optional, and named: makes string matching case-sensitive. Written by
name after the positional arguments, it needs no nth before
it (findvalue(..., exact: true)); the older positional
spelling, TRUE after an explicit nth, still
works.
findvalue$() returns a string value. Both
found_result and not_found_result must be
strings.
The most important rule is that found_result is evaluated
at the matched row.
Sheerpower finds the matching row, positions the cluster to that row, evaluates
found_result, and then restores the original row position. After the
call, the cluster's current row is exactly what it was before the call; only
_integer reflects the matched row number.
This returns Bob's salary, not the salary from the row that happened to be current before the call.
This is what makes findvalue() more than a simple
existence test. It is a lookup-and-return operation.
The not_found_result argument is required for both
findvalue() and findvalue$().
This forces every call site to make the not-found behavior visible. There is no hidden default and no silent assumption.
0 or "" when absence and empty mean
the same thing.
A traditional lookup with findrow() looks like this:
With findvalue(), the same intent becomes one
expression:
The shorter form is not just more compact. It removes the row-number bookkeeping and says directly what the program means.
Problem: Repeated findrow() code makes the
developer write mechanical lookup steps instead of the business
intent.
Solution: findvalue() and
findvalue$() express the lookup and returned value in
one readable expression.
Efficiency: Because Sheerpower clusters can search on any field, this works without extra lookup tables, declarations, or special key/value conventions.
Takeaway: When you want data from the matched row, use
findvalue() or findvalue$() instead of
manually finding the row and testing it.
findvalueby() and findvalueby$() work
exactly like findvalue() and findvalue$()
described above — same found and not-found results, same
_integer side effect, same restored row position, same
optional nth and case_sensitive arguments.
The one difference: the row is found using the same two-field
intersecting search that findrowby() performs, so the
matched row is the one where both field/value pairs
match.
findvalue() returns a numeric value from a matched row.
findvalue$() returns a string value from a matched row.
findvalueby() and findvalueby$() do the
same, but find the row with findrowby()'s two-field
intersecting search — the matched row is the one where both
field/value pairs match.
The search fields may be numeric or string. The $ suffix
on findvalue$() and findvalueby$()
describes the returned value, not the searched values.
The found result is evaluated after the matching row is found and the cluster is positioned to that row.
The not-found result is required, so every lookup clearly states what happens when the row is absent.
(Show/Hide Sheerpower FINDVALUE() and FINDVALUEBY() Takeaways)findvalue() returns a numeric value from a matched
row.
findvalue$() returns a string value from a matched
row.
findvalueby() and findvalueby$() are the
same functions with findrowby()'s two-field
intersecting search — both pairs must match.
$ suffix controls the return type, not the
search type.
nth argument selects which duplicate
match to use.
exact: true makes string matching
case-sensitive.
findrow() is a core language feature for fast,
predictable data access, not a helper routine or external index.
findrow() removes an entire class of application-level
indexing code while remaining simple to read and reason about.
|
Hide Description
|
|
|
Enter or modify the code below, and then click on RUN |
|
Looking for the full power of Sheerpower?
Check out the Sheerpower website. Free to download. Free to use. |