ns_set - Data Structure for Multimap with Potentially Case-insentitive Key-value Pairs
This command provides functions for manipulating sets of key-value pairs, or "fields". Fields in the set are ordered numerically, starting at zero and increasing by one up to the total number of fields. An ns_set is actually a multimap (also called multiset, or bag), meaning the same key can appear multiple times within the same set. Optionally, sets can be defined to be case-insensitive, meaning that mixed and uppercase keys are treated exactly like lowercase keys (for details, see Case Sensitivity).
The ns_set data structure is especially useful for handling data like HTTP headers, which are per definition case insensitive and may have repeated keys.
Returns the contents of the setId in a string format similar to Tcl's array-get syntax. This is helpful for iterating over key-value pairs with foreach. The result can also be used for creating a Tcl dict or a Tcl array. Note that since ns_set can contain duplicate keys, these conversion may lose duplicates, as keys in dicts and arrays are unique.
# Ouput HTTP request headers as string set headers [ns_conn headers] set output "" foreach {key value} [ns_set array $headers] { append output "$key = $value \n" } set output
Deletes all sets in the current interpreter. See also the section Storage Management below.
Returns a new set with the same name and contents as the specified setId. See also the section Storage Management below.
Conditionally appends a new field to the set with the specified key and value, only if the key does not already exist in the set. Returns the field number of the newly added field, or the field number of the existing field if it already exists.
Creates a new set and returns its setId.
If a name is provided, it sets the name of the set. You can also initialize the set with key/value pairs by providing them as additional arguments.
If the -nocase option is used, the set becomes case-insensitive for all commands that accept a key parameter. Keys are stored in case-insensitive sets in lowercase and returned in lowercase.
# Create a case-insensitive set with initial values set mySet [ns_set create -nocase mySetName a 1 B 2 c 3]
Deletes the field in the set at the specified field number.
Removes the first occurrence of a field in the set with the specified key. Note that multiple fields with the same key may exist; this command only removes the first occurrence.
Returns the index of the first field in the specified setId with a matching key. If no matching field is found, returns -1.
Formats the specified set in a human-friendly way and returns the string as result. Option -noname will omit the name from the output. When lead is specified, every key in the output will be preceded by this lead string (default is ""). When separator is specified, every key in the output will be followed by this separator string (default is ": ").
# Example of ns_set format % set mySet [ns_set create mySetName a 1 b 2 c 3] % ns_set format $mySet mySetName: a: 1 b: 2 c: 3 # Using options % ns_set format -noname -lead "-> " -separator " = " $mySet -> a = 1 -> b = 2 -> c = 3
Frees (deletes) the specified set. Although sets are automatically freed at the end of the request (see section Storage Management), freeing sets manually can save temporary memory within loops with many iterations.
Returns the first value associated with the specified key. If the key is not found, the default value or an empty string is returned. With the -all option, all values for the key are returned as a list.
% set mySet [ns_set create mySetName a 1 b 2 c 3 a 4] # Get the first value for key 'a' % ns_set get $mySet a 1 # Get all values for key 'a' (if there are duplicates) % ns_set get -all $mySet a 1 4 # Case-insensitive get % ns_set get -nocase $mySet A 1
Returns 1 if the field specified by fieldNumber is null, and 0 if it is not. Note that an empty string is distinct from null; this command returns 0 for an empty string.
Returns the key for the field at the specified fieldNumber.
Returns a list of all keys in the specified setId. If a pattern is provided, only keys matching the pattern are returned, following Tcl's string match rules.
Returns a list of all ns_set instances in the current interpreter.
Merges setId2 into setId1. Fields from setId2 are appended to setId1 only if their keys do not already exist in setId1.
# Example of ns_set merge # Create two sets % set set1 [ns_set create key1 value1 key2 value2] % set set2 [ns_set create key2 value2_new key3 value3] # Merge set2 into set1 % ns_set merge $set1 $set2 # Now set1 contains key1, key2 (original value), and key3 % ns_set keys $set1 key1 key2 key3
Moves all fields from setId2 to the end of setId1, leaving setId2 as an empty but valid set.
# Example of ns_set move # Create two sets % set set1 [ns_set create key1 value1] % set set2 [ns_set create key2 value2 key3 value3] # Move fields from set2 to set1 % ns_set move $set1 $set2 # Now set1 contains key1, key2, key3 % ns_set keys $set1 key1 key2 key3 # set2 is empty % ns_set size $set2 0
Returns the name of the set or an empty string if no name has been set.
Appends a new field with the specified key and value to the set and returns the field number of the new field. Use ns_set update if you want to update a field with a certain key.
Returns the number of fields in the set.
Splits one set into multiple sets based on a specific character, splitChar used in the key, and returns a list of IDs for the newly created sets. Defaults to using a period (".") as the split character.
# Create a set with keys containing periods % set s1 [ns_set create] % ns_set put $s1 dog.food "Yummy dog food!" % ns_set put $s1 cat.food "Yummy cat food!" % ns_set format $s1 dog.food: Yummy dog food! cat.food: Yummy cat food! # Split the set into multiple (actually 2) sets based on '.' in the key % set newSets [ns_set split $s1] d2 d3 # Show the result of the created new sets % set result "" % foreach s $newSets { % append result [ns_set format $s] % } % set result dog: food: Yummy dog food! cat: food: Yummy cat food!
Removes all fields from fieldNumber onward.
Returns 1 if the specified key is unique in the set and 0 if it is not.
# Create a set with duplicate keys % set headers [ns_set create] % ns_set put $headers Accept "text/html" % ns_set put $headers Accept "application/xml" % ns_set put $headers Host "www.example.com" # Check if 'Accept' header is unique % ns_set unique $headers Accept 0 # Check if 'Host' header is unique % ns_set unique $headers Host 1
Updates the first occurrence of a field with the specified key, replacing its value with the new value. Equivalent to ns_set delkey followed by ns_set put.
Returns the value of the field at the specified fieldNumber.
Returns a list of all values in the set. If a pattern is specified, only values matching the pattern are returned.
The option -nocase is valid for multiple commands. The options valid for just a single command are described above.
Basic operations on ns_sets:
# Create an ns_set with several fields, named "mySetName" % set mySet [ns_set create mySetName a b c d e f A Joe B John C Jeff] % ns_set size $mySet 6 # Get the name of the ns_set % ns_set name $mySet mySetName # Get all keys and values of the ns_set % ns_set array $mySet a b c d e f A Joe B John C Jeff % ns_set keys $mySet a c e A B C % ns_set values $mySet b d f Joe John Jeff # Get the value of the field with key 'A' % ns_set get $mySet A Joe # Get the value of the field with key 'a' % ns_set get $mySet a b # Is the key 'a' unique in the ns_set? % ns_set unique $mySet a 1 # Is the key 'a' unique in the ns_set, when the case is ignored? % ns_set unique -nocase $mySet a 0 # Truncate the set after the third field % ns_set truncate $mySet 3 % ns_set format $mySet mySetName: a: b c: d e: f # Update the value of the field with key "c" % ns_set update $mySet c "Hello World!" 2 % ns_set format $mySet mySetName: a: b c: Hello World! e: f # Lookup position of existing key % ns_set find $mySet c 1 # Lookup position of non-existing key % ns_set find $mySet nokey -1 # Delete the field at position 0 % ns_set delete $mySet 0 % ns_set format $mySet mySetName: c: Hello World! e: f
Iterating over the fields of an ns_set:
# Run through an ns_set based on key/value pairs foreach {key value} [ns_set array $myset]} { ns_log notice "key: $key, value: $value" } # Run through an ns_set based on indicies for {set i 0} {$i < [ns_set size $myset]} {incr i} { set key [ns_set key $myset $i] set value [ns_set value $myset $i] ns_log notice "key: $key, value: $value" }
Sets can be defined to be case-sensitive (default) or case-insensitive. When a set is defined case-insensitive, all keys are converted internally to lowercase. Every lookup operation with an upper or mixed case key will then succeed when the key in lowercase exists.
Case-sensitive and case-insensitive sets can be created either from Tcl or from C. To create a case-insensitive set from Tcl, use the option -nocase for the ns_set create command. When a case-insensitive set is duplicated via ns_set copy or ns_set split, the resulting set is also case-insensitive. The sets used for HTTP request headers and configuration values are created automatically from C as case-insensitive.
Even, when a set is defined as case-sensitive, all subcommands of ns_set receiving a key as input include the option -nocase, to make a single lookup case-insensitive.
Legacy: previous versions of NaviServer have used special names for the subcommands where the keys should be treated case-insensitive. These subcommand names were prefixed with the character "i" and were named "icput", "idelkey", "ifind", "iget", "imerge", "iunique", and "iupdate". These names are still supported for compatibility, but it is recommended to use the option -nocase instead.
Sets created via the Tcl interface using ns_set and the subcommands create, copy, and split are automatically deleted after each request, or when ns_set cleanup is executed. The cleanup is performed automatically after every request by the command ns_cleanup.
It is also possible to delete a specified set with the command ns_set free.
NULL values, distinct from empty strings, are useful when representing a database row as an ns_set. Currently, obtaining a NULL value in an ns_set through the Tcl API can be challenging. NULL values may occur in sets created by ns_parsequery or by omitting the last value when seeding an ns_set in the create subcommand, among other scenarios. NULL-valued fields can also be created through the C API.
ns_set is intended for relatively small amounts of data. Since keys are found by linear search, loading very large datasets into an ns_set may result in poor performance. In such cases, consider using a Tcl array or dict structure instead.