Using the generic TList collection: Is there some function that detects duplicate records or do I need to do a search on all records and check single field if duplicated or not?
The generic TList has a Contains method that can be used to detect duplicates. But you have to call this yourself as there is no Duplicates property as there is for TStringList.
If your data can be compared with a binary compare then there is nothing more to do. Otherwise you need to supply a custom comparer.
Related
Question
I am wondering if there is any way I can move to a specific record in a DataSet (FDQuery) by knowing only the Primary Key of the table.
What I know
I know I can move to the next/prior record by using FDQuery.Next; FDQuery.Prior; or move to a specific record number using FDQuery.RecNo := 2.
In my case, I just know the Primary Key (id) of the item and I want to move to this specific record that has the same id as mine.
To make it clear I can achieve this by iterating through the records, however, I would like to know if the is a way to move directly to the record, without needing to iterate through all the records.
with FDQuery do
begin
First;
while not Eof do
begin
if FieldByName(C_ID).AsInteger = IDAsPrimaryKey then
// Found!
Break;
Next;
end;
end;
The simplest way to do this is to use the Locate boolean function, as in
if FDQuery.Locate(C_ID, IDAsPrimaryKey, []) then
// do whatever
See e.g. http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_Locate for more info.
Note that Locate will accept a ;-separated list of field names as the first argument. In that case, the second argument needs to be a variant array of field values, which you can construct at run-time using the VarArrayOf function.
Btw, Locate is defined in the TDataSet class, but it is up to the authors of a given descendant library like FireDAC whether and how it is implemented in specific component classes.
You could also use the GoToKey or FindKey methods - see http://docwiki.embarcadero.com/RADStudio/Rio/en/Executing_a_Search_with_Goto_Methods. You may find one or other of these to be quicker than Locate, especially if your dataset already has a client-side index on the primary key but Locate is generally more convenient and concise, because the others are more long-winded to use.
I have a to-many relationship, where a List can have many Items.
I then have a List variable, whith the list Entity that I need. No problems there.
The list is a tableview and the user can add rows, so when they add a row it adds it as an Item relationship to the List.
I can then use List.valueForKey("item") to get the Items. But the return type is AnyObject. Is there a better way than casting it to an NSSet and then having to go through and grt the values and putting them into an array?
In other words, how can I get the value of all the Item entities and put them into an array ?
Pd: the Relationship is Ordered.
You cannot successfully cast something to something it is not. Casting is merely a way of revealing the truth to the compiler. The implication is that you know more than the compiler does. But you must tell the compiler the truth! If you lie to the compiler, you will crash at runtime.
So, if casting to an array causes a crash at runtime, that's because this is not an array. You can only cast to what it really is.
I need a delphi key/value collection that will allow me to iterate over the collection in the same order the key/value pairs were inserted/added.
TList<T> guarantees order but TDictionary<T1, T2> does not.
I guess I could always define a TList<TPair<Key, Value>> but it would be more cumbersome to work with.
Is there a built-in collection type that would meet my requirements or would wrapping TList<TPair<Key, Value>> be my best option? Or perhaps it would be better to have a TList<Key> and a TDictionary<Key, Value> and iterate through the list.
If your key type is string and your value type is some descendant of TObject, use a TStringList. Store your values in the Objects array property.
SL.AddObject('foo', obj1);
SL.Add('bar');
i := SL.IndexOf('bar');
SL.Objects[i] := obj2;
Set the OwnsObjects property if you need to.
the DeHL collections library contains a lot of "Ordered Dictionary"-like classes. The ordered ones use trees (which have order) instead of hash maps which are unordered.
I believe the TSortedDistinctMultiMap might be what you need, if you want to enforce uniqueness, and if you don't want to enforce Key value uniqueness, then there are other choices (without Distinct in the class name) that will be close to what you need.
Update 2017: The DeHL library is no longer maintained.
The Spring4D library provides dictionaries that are ordered. At the time of writing these are only available on the develop branch.
I need to compare two XDocument objects together. Unfortunately there are known differences between them, so a direct object comparison wont work. What i need is a way to recurse through every element and attribute of the xml document and compare their respective values, whilst ignoring the ones that are known to be different.
I know the names of the attributes that are known to be different (time date fields amongst others). What is the best strategy for achieving this?
if the XDocument objects are structurally equivalent, you could simply walk through the two hierarchies (using Descendant property, you can simply iterate). Then for each element returned, you can enumerate attributes.
at each step, if you get some element mismatch, you already know that your documents are different, else check attributes, skipping those that you know to be 'ignorable', continue iteration until end of Descendant collection.
I'm simply curious as lately I have been seeing the use of Hashmaps in Java and wonder if Delphi's Sorted String list is similar at all.
Does the TStringList object generate a Hash to use as an index for each item? And how does the search string get checked against the list of strings via the Find function?
I make use of Sorted TStringLists very often and I would just like to understand what is going on a little bit more.
Please assume I don't know how a hash map works, because I don't :)
Thanks
I'm interpreting this question, quite generally, as a request for an overview of lists and dictionaries.
A list, as almost everyone knows, is a container that is indexed by contiguous integers.
A hash map, dictionary or associative array is a container whose index can be of any type. Very commonly, a dictionary is indexed with strings.
For sake of argument let us call our lists L and our dictionaries D.
Lists have true random access. An item can be looked-up in constant time if you know its index. This is not the case for dictionaries and they usually resort to hash-based algorithms to achieve efficient random access.
A sorted list can perform binary search when you attempt to find a value. Finding a value, V, is the act of obtaining the index, I, such that L[I]=V. Binary search is very efficient. If the list is not sorted then it must perform linear search which is much less efficient. A sorted list can use insertion sort to maintain the order of the list – when a new item is added, it is inserted at the correct location.
You can think of a dictionary as a list of <Key,Value> pairs. You can iterate over all pairs, but more commonly you use index notation to look-up a value for a given key: D[Key]. Note that this is not the same operation as finding a value in a list – it is the analogue of reading L[I] when you know the index I.
In older versions of Delphi it was common to coax dictionary behaviour out of string lists. The performance was terrible. There was little flexibility in the contents.
With modern Delphi, there is TDictionary, a generic class that can hold anything. The implementation uses a hash and although I have not personally tested its performance I understand it to be respectable.
There are commonly used algorithms that optimally use all of these containers: unsorted lists, sorted lists, dictionaries. You just need to use the right one for the problem at hand.
TStringList holds the strings in an array.
If you call Sort on an otherwise unsorted (Sorted property = false) string list then a QuickSort is performed to sort the items.
The same happens if you set Sorted to true.
If you call Find (or IndexOf which calls find) on an unsorted string list (Sorted property = false, even if you explicitly called Sort the list is considered unsorted if the Sorted property isn't true) then a linear search is performed comparing all strings from the start till a match is found.
If you call Find on a sorted string list (Sorted property = true) then a binary search is performed (see http://en.wikipedia.org/wiki/Binary_search for details).
If you add a string to a sorted string list, a binary search is performed to determine the correct insertion position, all following elements in the array are shifted by one and the new string is inserted.
Because of this insertion performance gets a lot worse the larger the string list is. If you want to insert a large number of entries into a sorted string list, it's usually better to turn sorting off, insert the strings, then set Sorted back to true which performs a quick sort.
The disadvantage of that approach is that you will not be able to prevent the insertion of duplicates.
EDIT: If you want a hash map, use TDictionary from unit Generics.Collections
You could look at the source code, since that comes with Delphi. Ctrl-Click on the "sort" call in your code.
It's a simple alphabetical sort in non-Unicode Delphi, and a slightly more complex Unicode one in later versions. You can supply your own comparison for custom sort orders. Unfortunately I don't have a recent version of Delphi so can't confirm, but I expect that under the hood there's a proper Unicode-aware and locale-aware string comparison routine. Unicode sorting/string comparison is not trivial and a little web searching will point out some of the pitfalls.
Supplying your own comparison routine is often done when you have delimited text in the strings or objects attached to them (the Objects property). In those cases you often wat to sort by a property of the object or something other than the first field in the string. Or it might be as simple as wanting a numerical sort on the strings (so "2" comes after "1" rather than after "19")
There is also a THashedStringList, which could be an option (especially in older Delphi versions).
BTW, the Unicode sort routines for TStringList are quite slow. If you override the TStringList.CompareStrings method then if the strings only contain Ansi characters (which if you use English exclusively they will), you can use customised Ansi string comparisons. I use my own customised TStringList class that does this and it is 4 times faster than the TStringList class for a sorted list for both reading and writing strings from/to the list.
Delphi's dictionary type (in generics-enabled versions of Delphi) is the closest thing to a hashmap, that ships with Delphi. THashedStringList makes lookups faster than they would be in a sorted string list. you can do lookups using a binary search in a sorted stringlist, so it's faster than brute force searches, but not as fast as a hash.
The general theory of a hash is that it is unordered, but very fast on lookup and insertion. A sorted list is reasonably fast on insertion until the size of the list gets large, although it's not as efficient as a dictionary for insertion.
The big benefit of a list is that it is ordered but a hash-lookup dictionary is not.