Next release of kbmMW will contain a new feature that allows for making already short statements even shorter.

The feature was implemented on basis of a question on one of the social medias, where a person would like to get the average of a series of numbers embedded in a string, each number separated by a semicolon ‘;’.

Immediately I thought of LINQ, and wrote up a small sample for the user, which looks like this:

Short hand sample

procedure TForm1.Button1Click(Sender: TObject);
var
sl:TStringList;
ls:IkbmMWLinqStage;
begin
sl:=TStringList.Create('"',';');
try
sl.DelimitedText:='100.65;200.90;300.10';
ls:=linq.Using(sl,[mwlusoNumericValue]).Shared;
ShowMessage(Format('Min:%f Max:%f Avg:%f',
[single(ls.Min),single(ls.Max),ls.Avg]));
finally
sl.Free;
end;
end;

As you can see, I use a TStringList to load the string containing the numbers. As kbmMW LINQ can work with a TStringList, then it was simple to pick out min, max and avg values.

However it did bother me to have to use a TStringList to do the splitting up of data. I could have used a TkbmMWCSV Object Notation streamer, as the data is essentially “comma” separated values (except using a semicolon instead of a comma. But it would not be shorter to write, as I would still need to instantiate a TkbmMWCSV instance, setup a property or two and load the data, before I could provide the loaded object notation object to LINQ to do the calculations.

In fact LINQ does support UsingCSV statements to make it a single line load, but unfortunately it only supports that with CSV contents that are using commas for separators, as it did not allow for any configuration of the load process within the LINQ statement itself.

Since the LINQ Usingxxxx statements already contains various arguments, and various data parsers have many different possible settings available, I did not feel for polluting the Usingxxxx statements with additional arguments, which would also conflict with the various overloaded variants of the Using statements.

So some rethinking was required, which, inspired by the use elsewhere in kbmMW, ended up looking like this:

Shortened shorthand sample

procedure TForm1.Button1Click(Sender: TObject);
var
ls:IkbmMWLinqStage;
begin
ls:=linq.WithSettings('recordDelimiterChar1:";"')
.UsingCSV('100.65;200.90;300.10').Shared;

ShowMessage(Format('Min:%f Max:%f Avg:%f',
[single(ls.Min),single(ls.Max),ls.Avg]));
end;

The introduction of the WithSettings LINQ keyword. It allows for settings provided as a simplified YAML formatted string, in the same way as settings are provided elsewhere in kbmMW, for example in REST attributes and elsewhere.

In the above example I simply use the UsingCSV, but only after I have already specified that I want the record delimiter character to be a semicolon. More settings can be provided by separating each propertyName:propertyValue by a comma.

What is also a cool side effect of this, is that the setting string supports picking the settings up from a configuration storage (see Configuration and Configuration 2).

Hence it is possible to write:

procedure TForm1.Button1Click(Sender: TObject);
var
ls:IkbmMWLinqStage;
begin
ls:=linq.WithSettings('recordDelimiterChar1:"$(delimiters.myRecordDelimiter=;)"')
.UsingCSV('100.65;200.90;300.10').Shared;
ShowMessage(Format('Min:%f Max:%f Avg:%f',
[single(ls.Min),single(ls.Max),ls.Avg]));
end;

In this case it will first look for the settings in the configuration storage at the path delimiters.myRecordDelimiter. If nothing is found there it will default to use ; (semicolon).

I don’t know how often one would use the configuration based settings of LINQ, but I assume there could be uses for it, which thus makes it much easier to reconfigure your application for variations of a situation by simply changing an INI file, a registry setting, a JSON, XML or YAML file or a database whatever you are using for your configuration storage.

The following lists the more important settings available for some of the typical Usingxxx variants:

headertrue/false (default false)
recordDelimiterChar1record delimiter. Default \r (CR)
recordDelimiterChar2optional 2. record delimiter character. Default \n (LF)
fieldDelimiterCharfield delimiter. Default , (comma)
quoteCharstring quoting character. Default ” (quotation mark)
autoDetectTypetrue/false (default true). If true will try to detect datatype based on contents for all read data for that particular field.
defaultNamePrefixprefix for field names if read without header. Default FLD.
skip0..n, default 0. How many records after optional header, should be skipped before reading data.
skipBeforeHeader0..n, default 0. How many lines of text to skip before optional header or data.
dontExceptOnEmptyStringtrue/false, default false. If given an empty string to parse, should it except or return an empty dataset.
limit-1..n, default -1. Limit to number of data records to read.
trimtrue/false, default false. If to remove prefixed or postfixed spaces from column data if it is not quoted.
skipNoNameColumnstrue/false, default false. If to skip columns which have an empty name in the header.
UsingCSV
jQueryEscapingtrue/false, default false. Determines if to support jQuery escaped JSON data.
lateTypingtrue/false, default false. Determines if to read seemingly numeric data as a string.
dontExceptOnEmptyStringtrue/false, default false. If given an empty string to parse, should it except or return an empty dataset.
UsingJSON and UsingJSON5

If you like kbmMW, please let others know about it!

Loading

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.