Example of Cromis Library usage
What good is a Library of code if you have no use for it, or if the use is so clumsy you don’t want to use it. Almost all the code in Cromis Library was written out of my personal need to solve some problems. The main thing I strive to achieve besides speed and efficiency is ease of use and high level abstraction. I like code that on one hand writes almost like a script language and still gives you full power down to the metal on the other. Today I had to quickly write a program to solve the following problem:
I had two directories full of XML files. Each XML represents a survey. In one directory I had completed surveys and in the other I had sessions of those surveys. Each survey can be made in one or more sessions. The only ID I could match between them was SampleID (incremental number of the survey). I had some missing data for some external data linking, so I had to pull session ID from session XML files to the finished survey files. The steps are like that.
- Go through all the session files
- Sort them by save time
- Store the file names in cardinal hash table. If two or more exist with same SampleID, only use the one that is the oldest. That is why the sort.
- Go through all the finished surveys files
- For each XML check if the data is missing and if it is get it from hash table
- If file was changed write it back to HD
Ok you may think this should be a lot of code to write. Well it is not if you use my tools as I did. I uses AnyValue, IAnyArray, SimpleStorage and Cardinal HashTable.
procedure TForm2.Button1Click(Sender: TObject); var TempHashList: TCardinalHashTable; TempArray: IAnyArray; ValueItem: PAnyValue; Document: IDocument; AnyValue: TAnyValue; SampleID: Integer; Answer: IElement; begin TempHashList := TCardinalHashTable.Create; try TempArray := CreateAnyArray; CreateCollection(eSourceTemp.Text).Get ( 'Parameters/SampleID', procedure(const Document: IDocument; const Element: IElement; const Data: Pointer) begin AnyValue := TAnyValue.Null; AnyValue['Timestamp'] := Document.Data.GetAttr('SaveDate').AsDateTime; AnyValue['SampleID'] := Document.Data.Get(['Parameters', 'SampleID']).AsInteger; AnyValue['FullName'] := Document.Path; AnyValue['Filename'] := Document.Name; TempArray.Push(AnyValue); end, nil ); TempArray.Sort ( function(Item1, Item2: PAnyValue): Integer begin Result := CompareDateTime(Item1^['Timestamp'].AsDateTime, Item2^['Timestamp'].AsDateTime); end ); for ValueItem in TempArray.Enum.Reverse do TempHashList.Item[ValueItem^['SampleID'].AsInteger] := ValueItem^; for Document in CreateCollection(eSourceData.Text) do begin if not Document.Data.Exists(['Answers', 'ZAPIS_WID']) then begin SampleID := Document.Data.Get(['Parameters', 'SampleID']).AsInteger; if TempHashList.ContainsKey(SampleID) then begin Answer := Document.Data.Ensure(['Answers', 'ZAPIS_WID']); Answer.EnsureAttr('et').AsInteger := 0; Answer.EnsureAttr('date').AsDateTime := Now; Answer.EnsureAttr('source').AsString := 'setvar'; Answer.EnsureAttr('srcid').AsInteger := 2; Answer.AsString := TempHashList.Item[SampleID]['Filename']; Document.SaveChanges; end; end; end; finally TempHashList.Free; end; end; |
Sure this could be done with something else also. Probably there are tools out there to achieve the same, or not. I don’t know. I wrote this as an example, on how to use the code, to solve problems with as little code as possible. The only thing I still lack, as I solve XML problems very often is a true script interface for all this so no actual compile would be needed. It would be nice to have
By the way do you even see in the code that I work with XML files in a directory or that I work with XML at all? Also the array of TAnyValue is very abstract, no a simple dynamic array.
And as I said in previous posts, TAnyValue is becoming the central powerhouse for all this. Tomorrow I will post the new version of TAnyValue and IAnyArray that now uses the new improved sliced array data structure.
David Heffernan wrote,
Sounds like you need to learn a good scripting language. Delphi seems like the wrong tool.
Link | March 12th, 2013 at 1:20 am
SilverWarior wrote,
Based on your code you first add all the data into AnyArray and then sort it. Wouldn’t it be more eficient if you would have been sorting the data upon adding it into the AnyArray (find proper location for data before adding it into the array).
Now I don’t know exactly how your AnyArray works but when working with lists you can save quite some time when you “sort insert” the data.
Link | March 12th, 2013 at 1:40 am
Iztok Kacin wrote,
@David
I develop a large surveying system that operates as a web server. It can generate XML and HTML rendered content, it has API supported data back-end, its own proxy etc. It is highly modularized consisting of many processes of which each one is heavily multithreaded. It works as a HTML web server or as call center server where clients written in free pascal connect to it with full VOIP support. These are just some highlights, cause the whole system is very large and complex. So tell me how will a scripting language help me in my work?
On the other hand I use as little NEW code to solve problems as possible. You surely know the DRY principle and my example is just that. Why not use tested library code when applicable. On the other hand such high level abstraction is only good solving problems like the one I had. As I wrote I am sure I could solve it in thousand other ways. If you know of a scripting language that can do what I did out of the box please do tell me. But I need to be able to write what I did in 15 minutes, because that is the time it took me to write and test the code in the example.
Link | March 12th, 2013 at 7:36 am
Iztok Kacin wrote,
@SilverWarior
Actually you are wrong
Let me clarify. I will write the big O notation for both approaches to see which one takes more time:
1. Push all, then sort: O(N) + O(LogN)
http://stackoverflow.com/questions/168891/is-it-faster-to-sort-a-list-after-inserting-items-or-adding-them-to-a-sorted-lis
We push all once and all are O(1) so for N elements its O(N). Then we do the QuickSort which is O(LogN).
2. Insert sorted using the binary search, then sort: >O(N) + O(LogN)
Its about the same, but the constant factor for the second is bigger. If you build array from scratch like I did sorting afterwards is slightly faster. If you insert in already build and sorted array then your proposal is faster.
See also the link bellow about the same question on SO.
If you truly want to build it faster and sorted you can use a heap, but then heap is not a dynamic array
http://stackoverflow.com/questions/168891/is-it-faster-to-sort-a-list-after-inserting-items-or-adding-them-to-a-sorted-lis
Link | March 12th, 2013 at 7:48 am
A. Bouchez wrote,
Nice code.
You may add some expressiveness by using late-binding to your types. Just like with OleAutomation.
With this syntax sugar, you can write something like:
AnyValueVariant.Timestamp := Document.Data.SaveDate;
Using variants will loose some performance (unless you hook the RTL, as we did), but code is as easy to read as with a scripting language. And most part of the process in your code is within XML process,
We allow this for our SynBigTable classes, and for our DB classes. This is very expressive. See http://blog.synopse.info/post/2011/07/01/Faster-variant-late-binding
Of course, you can have both approaches at same time. Using direct AnyValue work if speed is needed, then use late-binding via a variant access variable. Both won’t check the value at compile time – e.g. if Timestamp property does not exist, it will compile with no complain, but raise an exception at runtime.
Link | March 12th, 2013 at 8:47 am
Tranquilizer wrote,
nice code!
Link | March 12th, 2013 at 8:53 am
Iztok Kacin wrote,
I though about that once when writing SimpleStorage interfaces. I have filters there that I invoke like that:
Element.Filter(gzip).AsString := ‘gzip me’;
I wanted something like:
Element.Filter.gzip.AsString := ‘gzip me’;
I opted against it then, don’t know why anymore
I will check that again as having an option to be more expressive is certainly nice. Thanks for the comment and the tip. BTW you say you hooked RTL. I hooked few procedures that work with records in System.pas to get TAnyValue to be what I like it to be. But I am still a bit uneasy about hooking. It seems very stable, but I am just worried that it will backfire some day on some system
I had a problem with DEP last weak that I solved promptly by using VirtualAlloc in Detours.pas in addition to GetMem, but still. So what did you use for your hooks and how stable it is?
Link | March 12th, 2013 at 8:58 am
Iztok Kacin wrote,
Arnaud, why is your blog not on DelphiFeeds?
Link | March 12th, 2013 at 10:06 am
A. Bouchez wrote,
@Iztok
Our blog was a candidate since the beginning, was once refused as “pure annoncement blog”, do not know why.
There is a request pending since 2011 – see http://delphifeeds.uservoice.com/forums/14264-feedback/suggestions/2338512-add-http-blog-synopse-info-tag-blog
DelphiFeeds is working, but seems not to be maintained.
http://planet.objpas.org/ aggregator seems more maintained – but I did not receive any answer either when proposing our blog.
Link | March 12th, 2013 at 11:56 am
Iztok Kacin wrote,
That is laughable. The amount of pure “spam” blogs on DelphiFeeds is staggering. Half of blogs there do not do much more then repeat the news from others, or write two lines. Sad but true. Just look at the last Delphi anniversary thing. Do I really need to know that Delphi is one year older from 20+ people. I don’t think so.
Well I will raise the issue and see what can be done.
Link | March 12th, 2013 at 12:01 pm
dex wrote,
I’m just wondering, is AnyValue available as a separate download?
I mean, currently I’ve only found it in Cromis Library, but the rest of them (IPC,IMC etc) has it’s own page with it’s own download in addition to being present in that library.
I’m asking because the first time I noticed AnyValue I didn’t have a clue where to find the download as it wasn’t and isn’t listed on the downloadpage. I then got Cromis Library and hoped it was there (which it was).
Link | March 12th, 2013 at 1:14 pm
Iztok Kacin wrote,
@dex
It will be in a few days. I just need the time to set up the page for it. It was not available in the past because if was to small and insignificant part of the library. Now that has changed.
Link | March 12th, 2013 at 1:22 pm