kspread

kspread_functions_database.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998-2002 The KSpread Team
3 www.koffice.org/kspread
4 Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22// built-in database functions
23
24#include "functions.h"
25#include "valuecalc.h"
26#include "valueconverter.h"
27
28using namespace KSpread;
29
30// prototypes
31Value func_daverage (valVector args, ValueCalc *calc, FuncExtra *);
32Value func_dcount (valVector args, ValueCalc *calc, FuncExtra *);
33Value func_dcounta (valVector args, ValueCalc *calc, FuncExtra *);
34Value func_dget (valVector args, ValueCalc *calc, FuncExtra *);
35Value func_dmax (valVector args, ValueCalc *calc, FuncExtra *);
36Value func_dmin (valVector args, ValueCalc *calc, FuncExtra *);
37Value func_dproduct (valVector args, ValueCalc *calc, FuncExtra *);
38Value func_dstdev (valVector args, ValueCalc *calc, FuncExtra *);
39Value func_dstdevp (valVector args, ValueCalc *calc, FuncExtra *);
40Value func_dsum (valVector args, ValueCalc *calc, FuncExtra *);
41Value func_dvar (valVector args, ValueCalc *calc, FuncExtra *);
42Value func_dvarp (valVector args, ValueCalc *calc, FuncExtra *);
43Value func_getpivotdata (valVector args, ValueCalc *calc, FuncExtra *);
44
45// registers all database functions
46void RegisterDatabaseFunctions()
47{
48 FunctionRepository* repo = FunctionRepository::self();
49 Function *f;
50
51 f = new Function ("DAVERAGE", func_daverage);
52 f->setParamCount (3);
53 f->setAcceptArray ();
54 repo->add (f);
55 f = new Function ("DCOUNT", func_dcount);
56 f->setParamCount (3);
57 f->setAcceptArray ();
58 repo->add (f);
59 f = new Function ("DCOUNTA", func_dcounta);
60 f->setParamCount (3);
61 f->setAcceptArray ();
62 repo->add (f);
63 f = new Function ("DGET", func_dget);
64 f->setParamCount (3);
65 f->setAcceptArray ();
66 repo->add (f);
67 f = new Function ("DMAX", func_dmax);
68 f->setParamCount (3);
69 f->setAcceptArray ();
70 repo->add (f);
71 f = new Function ("DMIN", func_dmin);
72 f->setParamCount (3);
73 f->setAcceptArray ();
74 repo->add (f);
75 f = new Function ("DPRODUCT", func_dproduct);
76 f->setParamCount (3);
77 f->setAcceptArray ();
78 repo->add (f);
79 f = new Function ("DSTDEV", func_dstdev);
80 f->setParamCount (3);
81 f->setAcceptArray ();
82 repo->add (f);
83 f = new Function ("DSTDEVP", func_dstdevp);
84 f->setParamCount (3);
85 f->setAcceptArray ();
86 repo->add (f);
87 f = new Function ("DSUM", func_dsum);
88 f->setParamCount (3);
89 f->setAcceptArray ();
90 repo->add (f);
91 f = new Function ("DVAR", func_dvar);
92 f->setParamCount (3);
93 f->setAcceptArray ();
94 repo->add (f);
95 f = new Function ("DVARP", func_dvarp);
96 f->setParamCount (3);
97 f->setAcceptArray ();
98 repo->add (f);
99 f = new Function ("GETPIVOTDATA", func_getpivotdata); // partially Excel-compatible
100 f->setParamCount (2);
101 f->setAcceptArray ();
102 repo->add (f);
103}
104
105int getFieldIndex (ValueCalc *calc, Value fieldName,
106 Value database)
107{
108 if (fieldName.isNumber())
109 return fieldName.asInteger() - 1;
110 if (!fieldName.isString ())
111 return -1;
112
113 TQString fn = fieldName.asString();
114 int cols = database.columns ();
115 for (int i = 0; i < cols; ++i)
116 if (fn.lower() ==
117 calc->conv()->asString (database.element (i, 0)).asString().lower())
118 return i;
119 return -1;
120}
121
122// ***********************************************************
123// *** DBConditions class - maintains an array of conditions ***
124// ***********************************************************
125
126class DBConditions {
127 public:
128 DBConditions (ValueCalc *vc, Value database, Value conds);
129 ~DBConditions ();
132 bool matches (unsigned row);
133 private:
134 void parse (Value conds);
135 ValueCalc *calc;
136 Condition **cond;
137 int rows, cols;
138 Value db;
139};
140
141DBConditions::DBConditions (ValueCalc *vc, Value database,
142 Value conds) : calc(vc), cond(0), rows(0), cols(0), db(database)
143{
144 parse (conds);
145}
146
147DBConditions::~DBConditions () {
148 int count = rows*cols;
149 for (int r = 0; r < count; ++r)
150 delete cond[r];
151 delete[] cond;
152}
153
154void DBConditions::parse (Value conds)
155{
156 // initialize the array
157 rows = conds.rows() - 1;
158 cols = db.columns();
159 int count = rows*cols;
160 cond = new Condition* [count];
161 for (int r = 0; r < count; ++r)
162 cond[r] = 0;
163
164 // perform the parsing itself
165 int cc = conds.columns ();
166 for (int c = 0; c < cc; ++c)
167 {
168 // first row contains column names
169 int col = getFieldIndex (calc, conds.element (c, 0), db);
170 if (col < 0) continue; // failed - ignore the column
171
172 // fill in the conditions for a given column name
173 for (int r = 0; r < rows; ++r) {
174 Value cnd = conds.element (c, r+1);
175 if (cnd.isEmpty()) continue;
176 int idx = r * cols + col;
177 if (cond[idx]) delete cond[idx];
178 cond[idx] = new Condition;
179 calc->getCond (*cond[idx], cnd);
180 }
181 }
182}
183
184bool DBConditions::matches (unsigned row)
185{
186 if (row >= db.rows() - 1)
187 return false; // out of range
188
189 // we have a match, if at least one row of criteria matches
190 for (int r = 0; r < rows; ++r) {
191 // within a row, all criteria must match
192 bool match = true;
193 for (int c = 0; c < cols; ++c) {
194 int idx = r * cols + c;
195 if (!cond[idx]) continue;
196 if (!calc->matches (*cond[idx], db.element (c, row + 1))) {
197 match = false; // didn't match
198 break;
199 }
200 }
201 if (match) // all conditions in this row matched
202 return true;
203 }
204
205 // no row matched
206 return false;
207}
208
209
210// *******************************************
211// *** Function implementations start here ***
212// *******************************************
213
214// Function: DSUM
215Value func_dsum (valVector args, ValueCalc *calc, FuncExtra *)
216{
217 Value database = args[0];
218 Value conditions = args[2];
219 int fieldIndex = getFieldIndex (calc, args[1], database);
220 if (fieldIndex < 0)
221 return Value::errorVALUE();
222
223 DBConditions conds (calc, database, conditions);
224
225 int rows = database.rows() - 1; // first row contains column names
226 Value res( Value::Float );
227 for (int r = 0; r < rows; ++r)
228 if (conds.matches (r)) {
229 Value val = database.element (fieldIndex, r + 1);
230 // include this value in the result
231 if (!val.isEmpty ())
232 res = calc->add (res, val);
233 }
234
235 return res;
236}
237
238// Function: DAVERAGE
239Value func_daverage (valVector args, ValueCalc *calc, FuncExtra *)
240{
241 Value database = args[0];
242 Value conditions = args[2];
243 int fieldIndex = getFieldIndex (calc, args[1], database);
244 if (fieldIndex < 0)
245 return Value::errorVALUE();
246
247 DBConditions conds (calc, database, conditions);
248
249 int rows = database.rows() - 1; // first row contains column names
250 Value res;
251 int count = 0;
252 for (int r = 0; r < rows; ++r)
253 if (conds.matches (r)) {
254 Value val = database.element (fieldIndex, r + 1);
255 // include this value in the result
256 if (!val.isEmpty ()) {
257 res = calc->add (res, val);
258 count++;
259 }
260 }
261 if (count) res = calc->div (res, count);
262 return res;
263}
264
265// Function: DCOUNT
266Value func_dcount (valVector args, ValueCalc *calc, FuncExtra *)
267{
268 Value database = args[0];
269 Value conditions = args[2];
270 int fieldIndex = getFieldIndex (calc, args[1], database);
271 if (fieldIndex < 0)
272 return Value::errorVALUE();
273
274 DBConditions conds (calc, database, conditions);
275
276 int rows = database.rows() - 1; // first row contains column names
277 int count = 0;
278 for (int r = 0; r < rows; ++r)
279 if (conds.matches (r)) {
280 Value val = database.element (fieldIndex, r + 1);
281 // include this value in the result
282 if ((!val.isEmpty()) && (!val.isBoolean()) && (!val.isString()))
283 count++;
284 }
285
286 return Value (count);
287}
288
289// Function: DCOUNTA
290Value func_dcounta (valVector args, ValueCalc *calc, FuncExtra *)
291{
292 Value database = args[0];
293 Value conditions = args[2];
294 int fieldIndex = getFieldIndex (calc, args[1], database);
295 if (fieldIndex < 0)
296 return Value::errorVALUE();
297
298 DBConditions conds (calc, database, conditions);
299
300 int rows = database.rows() - 1; // first row contains column names
301 int count = 0;
302 for (int r = 0; r < rows; ++r)
303 if (conds.matches (r)) {
304 Value val = database.element (fieldIndex, r + 1);
305 // include this value in the result
306 if (!val.isEmpty())
307 count++;
308 }
309
310 return Value (count);
311}
312
313// Function: DGET
314Value func_dget (valVector args, ValueCalc *calc, FuncExtra *)
315{
316 Value database = args[0];
317 Value conditions = args[2];
318 int fieldIndex = getFieldIndex (calc, args[1], database);
319 if (fieldIndex < 0)
320 return Value::errorVALUE();
321
322 DBConditions conds (calc, database, conditions);
323
324 bool match = false;
325 Value result = Value::errorVALUE();
326 int rows = database.rows() - 1; // first row contains column names
327 for (int r = 0; r < rows; ++r)
328 if (conds.matches (r)) {
329 if (match) {
330 // error on multiple matches
331 result = Value::errorVALUE();
332 break;
333 }
334 result = database.element (fieldIndex, r + 1);
335 match = true;
336 }
337
338 return result;
339}
340
341// Function: DMAX
342Value func_dmax (valVector args, ValueCalc *calc, FuncExtra *)
343{
344 Value database = args[0];
345 Value conditions = args[2];
346 int fieldIndex = getFieldIndex (calc, args[1], database);
347 if (fieldIndex < 0)
348 return Value::errorVALUE();
349
350 DBConditions conds (calc, database, conditions);
351
352 int rows = database.rows() - 1; // first row contains column names
353 Value res;
354 bool got = false;
355 for (int r = 0; r < rows; ++r)
356 if (conds.matches (r)) {
357 Value val = database.element (fieldIndex, r + 1);
358 // include this value in the result
359 if (!val.isEmpty ()) {
360 if (!got) {
361 res = val;
362 got = true;
363 }
364 else
365 if (calc->greater (val, res))
366 res = val;
367 }
368 }
369
370 return res;
371}
372
373// Function: DMIN
374Value func_dmin (valVector args, ValueCalc *calc, FuncExtra *)
375{
376 Value database = args[0];
377 Value conditions = args[2];
378 int fieldIndex = getFieldIndex (calc, args[1], database);
379 if (fieldIndex < 0)
380 return Value::errorVALUE();
381
382 DBConditions conds (calc, database, conditions);
383
384 int rows = database.rows() - 1; // first row contains column names
385 Value res;
386 bool got = false;
387 for (int r = 0; r < rows; ++r)
388 if (conds.matches (r)) {
389 Value val = database.element (fieldIndex, r + 1);
390 // include this value in the result
391 if (!val.isEmpty ()) {
392 if (!got) {
393 res = val;
394 got = true;
395 }
396 else
397 if (calc->lower (val, res))
398 res = val;
399 }
400 }
401
402 return res;
403}
404
405// Function: DPRODUCT
406Value func_dproduct (valVector args, ValueCalc *calc, FuncExtra *)
407{
408 Value database = args[0];
409 Value conditions = args[2];
410 int fieldIndex = getFieldIndex (calc, args[1], database);
411 if (fieldIndex < 0)
412 return Value::errorVALUE();
413
414 DBConditions conds (calc, database, conditions);
415
416 int rows = database.rows() - 1; // first row contains column names
417 Value res = 1.0;
418 bool got = false;
419 for (int r = 0; r < rows; ++r)
420 if (conds.matches (r)) {
421 Value val = database.element (fieldIndex, r + 1);
422 // include this value in the result
423 if (!val.isEmpty ()) {
424 got = true;
425 res = calc->mul (res, val);
426 }
427 }
428 if (got)
429 return res;
430 return Value::errorVALUE ();
431}
432
433// Function: DSTDEV
434Value func_dstdev (valVector args, ValueCalc *calc, FuncExtra *)
435{
436 // sqrt (dvar)
437 return calc->sqrt (func_dvar (args, calc, 0));
438}
439
440// Function: DSTDEVP
441Value func_dstdevp (valVector args, ValueCalc *calc, FuncExtra *)
442{
443 // sqrt (dvarp)
444 return calc->sqrt (func_dvarp (args, calc, 0));
445}
446
447// Function: DVAR
448Value func_dvar (valVector args, ValueCalc *calc, FuncExtra *)
449{
450 Value database = args[0];
451 Value conditions = args[2];
452 int fieldIndex = getFieldIndex (calc, args[1], database);
453 if (fieldIndex < 0)
454 return Value::errorVALUE();
455
456 DBConditions conds (calc, database, conditions);
457
458 int rows = database.rows() - 1; // first row contains column names
459 Value avg;
460 int count = 0;
461 for (int r = 0; r < rows; ++r)
462 if (conds.matches (r)) {
463 Value val = database.element (fieldIndex, r + 1);
464 // include this value in the result
465 if (!val.isEmpty ()) {
466 avg = calc->add (avg, val);
467 count++;
468 }
469 }
470 if (count < 2) return Value::errorDIV0();
471 avg = calc->div (avg, count);
472
473 Value res;
474 for (int r = 0; r < rows; ++r)
475 if (conds.matches (r)) {
476 Value val = database.element (fieldIndex, r + 1);
477 // include this value in the result
478 if (!val.isEmpty ())
479 res = calc->add (res, calc->sqr (calc->sub (val, avg)));
480 }
481
482 // res / (count-1)
483 return calc->div (res, count - 1);
484}
485
486// Function: DVARP
487Value func_dvarp (valVector args, ValueCalc *calc, FuncExtra *)
488{
489 Value database = args[0];
490 Value conditions = args[2];
491 int fieldIndex = getFieldIndex (calc, args[1], database);
492 if (fieldIndex < 0)
493 return Value::errorVALUE();
494
495 DBConditions conds (calc, database, conditions);
496
497 int rows = database.rows() - 1; // first row contains column names
498 Value avg;
499 int count = 0;
500 for (int r = 0; r < rows; ++r)
501 if (conds.matches (r)) {
502 Value val = database.element (fieldIndex, r + 1);
503 // include this value in the result
504 if (!val.isEmpty ()) {
505 avg = calc->add (avg, val);
506 count++;
507 }
508 }
509 if (count == 0) return Value::errorDIV0();
510 avg = calc->div (avg, count);
511
512 Value res;
513 for (int r = 0; r < rows; ++r)
514 if (conds.matches (r)) {
515 Value val = database.element (fieldIndex, r + 1);
516 // include this value in the result
517 if (!val.isEmpty ())
518 res = calc->add (res, calc->sqr (calc->sub (val, avg)));
519 }
520
521 // res / count
522 return calc->div (res, count);
523}
524
525// Function: GETPIVOTDATA
526// FIXME implement more things with this, see Excel !
527Value func_getpivotdata (valVector args, ValueCalc *calc, FuncExtra *)
528{
529 Value database = args[0];
530 int fieldIndex = getFieldIndex (calc, args[1], database);
531 if (fieldIndex < 0)
532 return Value::errorVALUE();
533 // the row at the bottom
534 int row = database.rows() - 1;
535
536 return database.element (fieldIndex, row);
537}
The ValueCalc class is used to perform all sorts of calculations.
Definition valuecalc.h:69
bool matches(const Condition &cond, Value d)
Returns true if value d matches the condition cond, built with getCond().
bool greater(const Value &a, const Value &b)
numerical comparison
void getCond(Condition &cond, Value val)
This method parses the condition in string text to the condition cond.
bool lower(const Value &a, const Value &b)
numerical comparison
Value add(const Value &a, const Value &b)
basic arithmetic operations
Provides a wrapper for cell value.
unsigned columns() const
If this value is an array, return the number of columns.
Value element(unsigned column, unsigned row) const
Return an element in the array value.
static const Value & errorDIV0()
Returns constant reference to #DIV/0! error.
unsigned rows() const
If this value is an array, return the number of rows.
bool isBoolean() const
Returns true if the type of this value is Boolean.
bool isEmpty() const
Returns true if empty.
bool isNumber() const
Returns true if the type of this value is either integer or floating-point.
long asInteger() const
Returns the integer value of this value.
TQString asString() const
Returns the string value of this value.
static const Value & errorVALUE()
Returns constant reference to #VALUE! error.
bool isString() const
Returns true if the type of this value is string.
The KSpread namespace.
Definition commands.h:39