I have a StringList with 2 rows
aOldPriceTerms[0] = 'FuelAddition=336643160'
aOldPriceTerms[1] = 'Freight=336643155'
So it works fine to use
aOldPriceTerms.Values['Freight'] -> '336643155'
I want to have a list of ID's from the list.
So simply
'FuelAddition','Freight'
Currently I use this code where aOldPriceTerms is the actual StringList.
function GetList(aOldPriceTerms: TStringList): String;
var
vOldTerm : string;
vOldTermsList : TStringList;
begin
vOldTermsList := TStringList.Create;
try
for i := aOldPriceTerms.Count - 1 downto 0 do
begin
vOldTerm := Copy(aOldPriceTerms[i], 1, (Pos('=', aOldPriceTerms[i])-1));
vOldTermsList.Add(vOldTerm);
end;
Result := vOldTermsList.CommaText;
finally
FreeAndNil(vOldTermsList);
end;
end;
My question is there a cleaner way to get the ids ?
Example is from the Delphi Basics, but TStringList.Names is also described in the Delphi documentation
// Now display all name and age pair values
for i := 0 to names.Count-1 do
begin
ShowMessage(names.Names[i]+' is '+names.ValueFromIndex[i]);
end;
You can use the TALNVStringList (NV for nameValue) from alcinoe (https://github.com/Zeus64/alcinoe) to handle Name and Value without all the time splitting the string
TALNVStringList is exactly the same as TStringList (nothing to change in the code except replacing TstringList by TALNVStringList) except that it's more efficient in speed because it's store the name in one dedicated field and the value in another dedicated field (no need to do all the time pos('=') and copy() to retrieve the name and the value of the row)
for i := 0 to aOldPriceTerms.Count-1 do
begin
ShowMessage(aOldPriceTerms.Names[i]+' is '+aOldPriceTerms.ValueFromIndex[i]);
end;
Exe demo showing the speed penalty of classic TstringList: https://svn.code.sf.net/p/alcinoe/code/demos/ALSortedListBenchmark/win32/ALSortedListBenchmark.exe
To get all the id's
for name in names.Names do
begin
i := names.IndexOf[name];
end;
or
To get all the Values
for name in vOldTermsList.Names do
begin
Value := vOldTermsList.Value[name];
end;
Related
Want to load name value pairs into a TStringList.
have a text file with data:
a=ant
a=animal
b=bob
d=darn
Function to load it:
LoadData(argPath: string): TStringList;
var
data: TStringList;
begin
data := TStringList.Create(true);
data.Delimiter := '=';
try
data.LoadFromFile(argPath);
except on E : Exception do
begin
Application.MessageBox(PWideChar(E.Message),
'Unable to Load Data', MB_OK or MB_ICONERROR);
Application.Terminate;
end;
end;
Result := data;
end;
Result is currently:
a=ant
a=animal
b=bob
d=darn
want the Result's strings to be:
ant
animal
bob
darn
and the Result's TObjects to be
a
a
b
d
After this I want to be able to display this in a TComboBox.
You'll have to use the built-in functions in the TStringList. I've broken this down into variables just for ease of reading / understanding, but could be compacted:
var
X: Integer;
Name, Value: String;
begin
for X:= 0 to MyList.Count - 1 do begin
Name:= MyList.Names[X];
Value:= MyList.ValueFromIndex[X];
MyCombo.Items.Add(Value);
end;
end;
With that, I'm sure you can figure out the rest (since I'm not sure exactly what you mean by result strings and objects).
It's preferable to pass the string list as a parameter, to ease lifetime management. It's also better to let the function raise exceptions and deal with them later. That allows more flexibility and the possibility of reuse. Like this:
procedure LoadValues(const FileName: string: Values: TStringList);
var
i: Integer;
begin
Values.LoadFromFile(FileName);
for i := 0 to Values.Count - 1 do
if Pos(Values.NameValueSeparator, Values[i]) <> 0 then
Values[i] := Values.ValueFromIndex[i];
end;
You cannot store strings directly in the Objects[] property of a TStrings because strings are managed types. You should probably store the names in a separate string list, or another more capable data structure. It's easy to extend the above code to populate two lists instead of one using the Names[] property.
It sounds like you are trying to stuff all of your data into a visual control. Don't do that. Treat visual controls as things to display data and not to hold and manage it.
Gave up on trying to load the combo box with LoadFromFile. Ended up creating two separate lists.
In FormCreate handler:
LoadData(...)
myComboBox.Items := displayList;
LoadData
procedure MyForm.LoadData(
const path: string; data: TStringList);
var index : Integer;
begin
data.LoadFromFile(path);
for index := 0 to data.Count - 1 do
begin
displayList.Add(data.ValueFromIndex[index]);
valueList.Add(data.Names[index]);
end;
end;
Whenever you need to get the selected value:
valueList[myComboBox.ItemIndex]
I get Access violation error, but I know the code is correct, so where could be the problem? I'm trying to fill a ComboBox whit data from a local AccessDB.
var i : integer;
x : string;
begin
with DataModule3.ADOTable1 do begin
if RecordCount > 0 then
for i := 1 to RecordCount do begin
RecNo := i;
x := FieldByName('Teacher').AsString;
ComboBox1.Items.Add(x);
end;
end;
end;
I have tried lots of things and nothing works, I have tried lots of combobox typed but still doesn't work the only time a combobx showed value was when I selected a row in table then it showed in combobox the rows value by which I need to filter...
Access Violation is raised most probably because you have forgot to instantiate your datamodule DataModule3. Verify this by calling Assigned function.
begin
with DataModule3.ADOTable1 do
if Active then
while not Eof do
begin
ComboBox1.Items.Add(FieldByName('Teacher').AsString);
Next;
end;
end;
This is related to another question but doesn't really fit enough to include it with the original. When a Post is called, how can I get the field (or fields) that was modified to a TField?
For logging, I use the OnBeforePost event, which is called (as it says) just before the data is posted. The drawback to this, of course, is that your log table has to have fields wide enough to hold all possible content.
procedure TMyData.SomeTableBeforePost(DataSet: TDataSet);
var
i: Integer;
begin
for i := 0 to DataSet.FieldCount - 1 do
begin
// Skip calculated and lookup fields
if DataSet.Fields[i].FieldType = ftData then
begin
if DataSet.Fields[i].OldValue <> DataSet.Fields[i].NewValue then
begin
LogTable.Insert;
LogTableColumnName.AsString := DataSet.Fields[i].FieldName;
LogTableOldValue.Value := DataSet.Fields[i].OldValue;
LogTableNewValue.Value := DataSet.Fields[i].NewValue;
LogTable.Post;
end;
end;
end;
end;
I have an ADO Connection String:
Provider=SQLOLEDB.1;Data Source=MYCOMPUTER\SQL2008;User ID=GuestUser;Password=password;Persist Security Info=True;Initial Catalog=DefaultDatabase;
And I want to read specific parts of the string into various TEdit controls. Is there a function that is able to parse that information for me or am I going to need to split strings at the semicolons, then again by the equal signs and read the first index of that string array?
If you just want to parse the string as it is you can use a StringList setting Delimiter and StrictDelimiter and assign DelimitedText. You may then use Names and Values of the StringList.
var
i:Integer;
sl:TStringList;
begin
sl:=TStringList.Create;
try
sl.Delimiter :=';';
sl.StrictDelimiter := true;
sl.DelimitedText := Con.ConnectionString;
Memo1.Lines.Assign(sl);
finally
sl.Free;
end;
end;
Access e.g. via sl.Names[i]; sl.Values['Password']; sl.ValueFromIndex[i]
Ado itself allows access through Properties which will list more than the assigned values and would be the preferable way.
var
i:Integer;
begin
For i := 0 to Con.Properties.Count - 1 do
begin
Memo1.Lines.Add(Con.Properties[i].Name + '='+ VarToStr(Con.Properties[i].Value));
end;
end;
Given a nested TClientDataSet, how could I find the link field name on the detail TClientDataSet?
I'm copying data from one TClientDataSet to another (record by record) and I would like to automatically ignore the link field.
I could also copy the data using the TClientDataSet.Data property but I still would need to clear the link and key fields.
You can check the "DataSetField" property of the detail/nested dataset, or access the "NestedDataSet" property of the master dataset.
Sample code to get the "link" field name:
function GetCDSDLinkFieldName(cds: TClientDataSet): string;
var
i: Integer;
cdsDetail: TClientDataSet;
begin
Result := EmptyStr;
cdsDetail := nil;
if Assigned(cds.DataSetField) then
cdsDetail := cds;
if not Assigned(cdsDetail) and (cds.FieldCount > 0) then
begin
i := 0;
while not Assigned(cdsDetail) and (i < cds.FieldCount) do
begin
if cds.Fields[i].DataType = ftDataSet then
cdsDetail := TClientDataSet(TDataSetField(cds.Fields[i]).NestedDataSet);
Inc(i);
end;
end;
if Assigned(cdsDetail) then
Result := cdsDetail.DataSetField.FieldName;
end;
Invoking example:
procedure ...
begin
ShowMessage(GetCDSDLinkFieldName(cdsMaster));
ShowMessage(GetCDSDLinkFieldName(cdsDetail));
end;
P.S.: 2 years later I don't believe this answer will help the author of the question, but maybe can help others that search for the same subject.