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.