omdl  v0.5
OpenSCAD Mechanical Design Library
map.scad
Go to the documentation of this file.
1 //! Mapped key-value pair data access.
2 /***************************************************************************//**
3  \file map.scad
4  \author Roy Allen Sutton
5  \date 2015-2017
6 
7  \copyright
8 
9  This file is part of [omdl] (https://github.com/royasutton/omdl),
10  an OpenSCAD mechanical design library.
11 
12  The \em omdl is free software; you can redistribute it and/or modify
13  it under the terms of the [GNU Lesser General Public License]
14  (http://www.gnu.org/licenses/lgpl.html) as published by the Free
15  Software Foundation; either version 2.1 of the License, or (at
16  your option) any later version.
17 
18  The \em omdl is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  Lesser General Public License for more details.
22 
23  You should have received a copy of the GNU Lesser General Public
24  License along with the \em omdl; if not, write to the Free Software
25  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26  02110-1301, USA; or see <http://www.gnu.org/licenses/>.
27 
28  \details
29 
30  Manage a collection of key-value pairs where keys are unique.
31 
32  \ingroup data data_map
33 *******************************************************************************/
34 
35 use <console.scad>;
36 include <primitives.scad>;
37 
38 //----------------------------------------------------------------------------//
39 /***************************************************************************//**
40  \addtogroup data
41  @{
42 
43  \defgroup data_map Map
44  \brief Mapped data access via key-value pairs.
45 
46  \details
47 
48  \b Example
49 
50  \dontinclude map_example.scad
51  \skip use
52  \until map_dump(map);
53 
54  \b Result \include map_example.log
55 
56  @{
57 *******************************************************************************/
58 //----------------------------------------------------------------------------//
59 
60 //! Return the index for the storage location of a map key-value pair.
61 /***************************************************************************//**
62  \param map <2d-vector> A two dimensional vector (2-tuple x n-tuple)
63  containing an associative map with n elements.
64  \param key <string> A map entry identifier.
65 
66  \returns <integer> The index of the value associated \p key in the map.
67  Returns \b undef if \p key is not a string or does not exists.
68 *******************************************************************************/
69 function map_get_idx
70 (
71  map,
72  key
73 ) = !is_string(key) ? undef
74  : let
75  (
76  i = first(search([key], map, 1, 0 ))
77  )
78  (i == empty_v) ? undef
79  : i;
80 
81 //! Test if a key exists in a map.
82 /***************************************************************************//**
83  \param map <2d-vector> A two dimensional vector (2-tuple x n-tuple)
84  containing an associative map with n elements.
85  \param key <string> A map entry identifier.
86 
87  \returns <boolean> \b true when the key exists and \b false otherwise.
88 *******************************************************************************/
89 function map_exists
90 (
91  map,
92  key
93 ) = (map_get_idx(map, key) != undef);
94 
95 //! Get the value associated with a map key.
96 /***************************************************************************//**
97  \param map <2d-vector> A two dimensional vector (2-tuple x n-tuple)
98  containing an associative map with n elements.
99  \param key <string> A map entry identifier.
100 
101  \returns <value> The map value associated with \p key.
102  Returns \b undef if \p key does not exists.
103 *******************************************************************************/
104 function map_get
105 (
106  map,
107  key
108 ) = second(map[map_get_idx(map, key)]);
109 
110 //! Get a vector of the map entry identifier keys.
111 /***************************************************************************//**
112  \param map <2d-vector> A two dimensional vector (2-tuple x n-tuple)
113  containing an associative map with n elements.
114 
115  \returns <vector> A vector of keys that exist in the associative map.
116 
117  \details
118 
119  \note Uses function \ref eselect to select the first column of the
120  vector defining the map.
121 *******************************************************************************/
122 function map_get_keys
123 (
124  map
125 ) = eselect(map, f=true);
126 
127 //! Get a vector of the map entry values.
128 /***************************************************************************//**
129  \param map <2d-vector> A two dimensional vector (2-tuple x n-tuple)
130  containing an associative map with n elements.
131 
132  \returns <vector> A vector of values stored in the associative map.
133 
134  \details
135 
136  \note Uses function \ref eselect to select the last column of the
137  vector defining the map.
138 *******************************************************************************/
139 function map_get_values
140 (
141  map
142 ) = eselect(map, l=true);
143 
144 //! Get the number of key-value pairs stored in a map.
145 /***************************************************************************//**
146  \param map <2d-vector> A two dimensional vector (2-tuple x n-tuple)
147  containing an associative map with n elements.
148 
149  \returns <integer> The number of key-value pairs stored in the map.
150 *******************************************************************************/
151 function map_size
152 (
153  map
154 ) = len(map);
155 
156 //! Perform some basic validation/checks on a map.
157 /***************************************************************************//**
158  \param map <2d-vector> A two dimensional vector (2-tuple x n-tuple)
159  containing an associative map with n elements.
160 
161  \param verbose <boolean> Be verbose during check.
162 
163  \details
164 
165  Check that: (1) each entry has key-value 2-tuple, (2) each key is a
166  string, and (3) key identifiers are unique.
167 *******************************************************************************/
168 module map_check
169 (
170  map,
171  verbose = false
172 )
173 {
174  if (verbose) log_info("begin map check");
175 
176  if (verbose) log_info ("checking map format and keys.");
177 
178  if ( map_size(map) > 0 )
179  for ( i = [0:map_size(map)-1] )
180  {
181  entry = map[i];
182  key = first(entry);
183 
184  // each entry has key-value 2-tuple.
185  if ( 2 != len(entry) )
186  {
187  log_error
188  (
189  str (
190  "map index ", i,
191  ", entry=", entry,
192  ", has incorrect count=[", len(entry),"]"
193  )
194  );
195  }
196 
197  // each key must be a string.
198  if ( is_string(key) == false )
199  {
200  log_error
201  (
202  str (
203  "map index ", i,
204  ", entry=", entry,
205  ", key=[", key,"] is not a string."
206  )
207  );
208  }
209 
210  // no repeat key identifiers
211  if ( len(first(search([key], map, 0, 0))) > 1 )
212  log_warn
213  (
214  str(
215  "map index ", i,
216  ", key=[", key,"] not unique."
217  )
218  );
219  }
220 
221  if (verbose)
222  {
223  log_info
224  (
225  str (
226  "map size: ",
227  map_size(map), " entries."
228  )
229  );
230 
231  log_info("end map check");
232  }
233 }
234 
235 //! Dump each map key-value pair to the console.
236 /***************************************************************************//**
237  \param map <2d-vector> A two dimensional vector (2-tuple x n-tuple)
238  containing an associative map with n elements.
239 
240  \param sort <boolean> Sort the output by key.
241  \param number <boolean> Output index number.
242  \param p <integer> Number of places for zero-padded numbering.
243 *******************************************************************************/
244 module map_dump
245 (
246  map,
247  sort = true,
248  number = true,
249  p = 3
250 )
251 {
252  if ( map_size(map) > 0 )
253  {
254  keys = map_get_keys(map);
255  maxl = max( [for (i = keys) len(i)] ) + 1;
256 
257  for (key = sort ? qsort(keys) : keys)
258  {
259  idx = map_get_idx(map, key);
260 
261  log_echo
262  (
263  str (
264  number ? chr(consts(p-len(str(idx)), 48)) : empty_str,
265  number ? str(idx, ": ") : empty_str,
266  chr(consts(maxl-len(key), 32)), "'", key, "' = ",
267  "'", map_get(map, key), "'"
268  )
269  );
270  }
271  }
272 
273  if ( number )
274  log_echo(str("map size: ", map_size(map), " entries."));
275 }
276 
277 //! @}
278 //! @}
279 
280 //----------------------------------------------------------------------------//
281 // openscad-amu auxiliary scripts
282 //----------------------------------------------------------------------------//
283 
284 /*
285 BEGIN_SCOPE example;
286  BEGIN_OPENSCAD;
287  use <map.scad>;
288 
289  map =
290  [
291  ["part1", ["screw10", [10, 11, 13]]],
292  ["part2", ["screw12", [20, 21, 30]]],
293  ["part3", ["screw10", [10, 10, -12]]],
294  ["config", ["top", "front", "rear"]],
295  ["version", [21, 5, 0]],
296  ["runid", 10]
297  ];
298 
299  map_check(map, true);
300 
301  echo( str("is part0 = ", map_exists(map, "part0")) );
302  echo( str("is part1 = ", map_exists(map, "part1")) );
303 
304  p1 = map_get(map, "part1");
305  echo( c=second(p1) );
306 
307  keys=map_get_keys(map);
308  parts = delete(keys, mv=["config", "version", "runid"]);
309 
310  for ( p = parts )
311  echo
312  (
313  n=p,
314  p=first(map_get(map, p)),
315  l=second(map_get(map, p))
316  );
317 
318  map_dump(map);
319  END_OPENSCAD;
320 
321  BEGIN_MFSCRIPT;
322  include --path "${INCLUDE_PATH}" {config_base,config_csg}.mfs;
323  include --path "${INCLUDE_PATH}" script_std.mfs;
324  END_MFSCRIPT;
325 END_SCOPE;
326 */
327 
328 //----------------------------------------------------------------------------//
329 // end of file
330 //----------------------------------------------------------------------------//
module map_check(map, verbose=false)
Perform some basic validation/checks on a map.
Definition: map.scad:169
module log_error(m)
Output error message to console.
Definition: console.scad:138
function qsort(v, r=false)
Sort the numeric or string elements of a vector using quick sort.
function second(v)
Return the second element of an iterable value.
function first(v)
Return the first element of an iterable value.
module log_info(m)
Output information message to console.
Definition: console.scad:94
module log_echo(m)
Output message to console.
Definition: console.scad:61
function map_get(map, key)
Get the value associated with a map key.
function map_exists(map, key)
Test if a key exists in a map.
function map_get_values(map)
Get a vector of the map entry values.
function eselect(v, f=true, l=false, i)
Select an element from each iterable value.
function map_get_idx(map, key)
Return the index for the storage location of a map key-value pair.
module log_warn(m)
Output warning message to console.
Definition: console.scad:109
empty_v
A vector with no content (the empty vector).
Definition: constants.scad:79
function is_string(v)
Test if a value is a string.
empty_str
A string with no content (the empty string).
Definition: constants.scad:76
function map_get_keys(map)
Get a vector of the map entry identifier keys.
function consts(l, v)
Create a vector of constant elements.
module map_dump(map, sort=true, number=true, p=3)
Dump each map key-value pair to the console.
Definition: map.scad:245
function map_size(map)
Get the number of key-value pairs stored in a map.