Skip to content

A data manager library that allows data handling of various types at runtime, allows filtering, queries and supports formula for automating things.

License

Notifications You must be signed in to change notification settings

keshav-sahu7/KMTableLib

Repository files navigation

KMTableLib

CMake on linux platforms

A library that allows easy handling of various types of data in tabular form and easy manipulation and automation of the data. It supports int (32 and 64 bit), float (at least 32 bit and 64 bit), string, boolean, date and date+time.

Table

This allows creating table with columns of different data types at run time. Each column will have a column name (column-id), display name (a description of the column) and a data type. Table can have multiple rows and columns.

Features of table

  • Supports multiple columns of different data types.
  • Very light weight on memory, data are stored in original form. Variants are used only for data exchange.
  • Can add new column as per need.
  • Supports various ways for populating newly create columns for existing rows.
  • Allows transformation of existing column using smart Formula

To create a table Person with name, age, date of birth and address.

using dt = km::DataType;
km::Table person("Person",{
    {"name", "Name", dt::STRING},
    {"dob", "Date of birth", dt::DATE},
    {"address", "Address", dt::STRING}
    }, km::SortingOrder::ASCENDING);

Inserting data in the table

person.insertRow({"Keshav Sahu", {1997, 9, 25}, "Raipur, Chhattisgarh"});
person.insertRow({"Yash Sanap", {2000, 9, 22}, "Mumbai, Maharashtra"});
person.insertRow({"Bishal Bhattacharya", {2004, 1, 21}, "Kathmandu, Nepal"});
person.insertRow({"Nitesh Giri", {2003, 2, 24}, "Kathmandu, Nepal"});

Now say we forgot to add a column, let say we want to add a colum age, but it already has rows should we add it manually? or should we fill it with a fixed value for all?

Well all these options are allowed but is there a way to automatically fill them according to its dob (date of birth) columns? Answer is yes, there is two way. First one (using a compile time function):

person.addColumnF({"age", "Age", km::DataType::INT32}, [&person](auto row_index){
    return km::KInt32(2022 - person.getDataWC(row_index, 1 /* dob column index*/).asDate().year)
});

Second one (using formula, so not bound with compile time)

// notice the $ sign before dob, $ is used to denote a column
person.addColumnE({"age", "Age", km::DataType::INT32}, "sub(2022, year($dob))");

If we print the data we would get

name                     dob           address                age
Bishal Bhattacharya   21/01/2004     Kathmandu, Nepal          18
Keshav Sahu           25/09/1997     Raipur, Chhattisgarh      25
Nitesh Giri           24/02/2003     Kathmandu, Nepal          19
Yash Sanap            22/09/2000     Mumbai, Maharashtra       22

Some drawbacks

  • Columns can not be deleted due to dependency reason.
  • No built in support for NULL values (will add later).

Formula

All tables and views can be automated with formulae. Formulae are case sensitive, type strict and compromises of only functions. It has some certain syntax, any integer number is treated as int32, but if followed with l or L then it is treated as int64. Any real number is treated as float64, but if it is followed by f then it is treated as float32. Any string between "" are treated as string while True and False are treated as boolean. There is no such literals for date and date_time.

It follows functional practices that means function must not have any side effects. It is type strict that's why it is too fast. But for convenience function overloading is supported.

add(10,20)                  // gives 30 (int32)
add(10.0f, 20.343389f)      // gives 20.343389 (float32)
add(10, 20.f);              //error as there is no add(int32, float32) function defined!

No operators are supported so even for basic works (such as basic arithmetic) we need to use functions. See functions directory. To refer a column in formula a $ sign before column name is used. It is safe as it can not access the data which is not in the current row (e.g. data from different row). Syntax of the formula is easy

    function_name(arg1, arg2, ...)
        where arguments can be a literal value or a $column_name or a function itself.

Note : You must call km::initAllFnc() to load the built in functions.

Formula can also be used to create Views and for running queries See BookLibrary Example.

There's a number of functions categorized as:

  • Arithmetic such as add, sub, mul, div
  • Comparison such as isEqual, isGreater, isLess
  • Date and DateTime such as day month year isLeapYear
  • Logical such as IF AND OR
  • String such as length toLower toUpper contains
  • Type conversion such as toInt32, toString, toDate, toDateTime

View

A view is just a virtual table so it doesn't consume extra space, it doesn't copy the actual data. We can create table of any view using a filter formula.

Features of a view :

  • A virtual table that means no extra space to copy the data.

  • Always up to date (auto refresh when source table is changed!)

  • Allows modification in sorting column and order.

  • Nested view is also possible.

Let's say we want to create a view of the previous "Person" table that watches for only persons who live in Chhattisgarh (whatever city) with only "name" and "address" columns.

km::BasicView view("view name", &person, {"name","address"}, "contains(toLower($address), \"chhattisgarh\")");
//view.rowCount() == 1
std::cout << view.getDataWC(/*row index = */ 0, /* column index = */ 0).asString();
//prints : Keshav Sahu

//insert a new row in the table
person.insertRow({"Tikeshwar Sahu", 26, {1996, 2, 23}, "Kurud Chhattisgarh"});

//view is auto refreshed
std::cout << "After new insertion in the table `Person`, view holds " << view.rowCount() << " rows." << std::endl;
//prints : After new insertion in the table `Person`, view holds 2 rows.

// changing address of "Keshav Sahu"
person.setData(1,3, "Bangalore, Karnataka");

std::cout << "After data update view holds " << view.rowCount() << "row." << std::endl;
//prints : After data update view holds 1 row.

Want to register your own function?

You need to create function and then register it with the FunctionStore instance.

#include <kmt/Core.hpp>     // for km::Variant
#include <kmt/FunctionStore.hpp> // for km::FunctionStore

// formula equivalent of boolean isEvenNumber(KInt32 num)
km::Variant isEvenNumber(const km::Variant *args)
{
    return km::KBoolean(args[0].asInt32() % 2 == 0 ? true : false);
}

// registering the function (typically in main cpp file)
km::FunctionStore::store().addEntry("isEvenNumber_i", {
    /*function_ptr = */ isEvenNumber,
    /*return_type = */ km::DataType::KBoolean,
    /*number of arguments */ = 1});

Notice the isEvenNumber_i, the _i denotes that it takes a single int32 arguments. If it was isEvenNumber_iI, it would be treated as isEvenNumber(int32, int64), i denotes int32, I denotes int64, f denotes float32, F denotes float64, s denotes string, b denotes boolean, d denotes date and D denotes date_time. See FunctionStore.hpp for more info.

For building the project visit How to build KMTableLib

About

A data manager library that allows data handling of various types at runtime, allows filtering, queries and supports formula for automating things.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published