Jump to content
dummzeuch

Which implementation of this is easier to understand?

Recommended Posts

6 hours ago, Stefan Glienke said:

Wrong, string and object are in the same entry - the internal storage of a TStringList has an array of TStringItem which is a record which contains a string and an object.

 

You simply find it like this:

 


function findSomething( const aStringArg : string ) : TObject;
var
  i: Integer;
begin
  i := sl.IndexOf(aStringArg);
  if i >= 0 then
    Result := sl.Objects[i]
  else
    Result := nil;
end;

Make this a method in your own TStringListHelper and there you go. If you want easy and fast lookup of objects by string key, then use a dictionary.

 

Dang, I never noticed that. I've tried everything I could think of but not this. Thanks!

 

But it would still be nice to have a way to get the index corresponding to an iterator position in a list somehow.

Share this post


Link to post
8 hours ago, Stefan Glienke said:

Make this a method in your own TStringListHelper and there you go.

Or better, a helper to TStrings

Share this post


Link to post
9 hours ago, David Schwartz said:

   for var str in sl do
      if (str = aStringArg) then
         Exit( sl.Objects[ ??? ];
         // oops ... no way to get the corresponding item in Objects!

JS is pretty cool here
 

for const [str, obj] of sl
  if (str == aStringArg)
    return obj;

 

I partially agree with you. I love for-in but the fact it hides current index makes it non-applicable in pretty much cases.

Edited by Fr0sT.Brutal
  • Like 1

Share this post


Link to post
4 hours ago, Darian Miller said:

'Seek' seems a little oddly named method here.

That name is based on the Seek function for a file of record. Usually the reader (we have many of these for various file types) is a thin wrapper around a file of record. Depending on the file type that "thinness" varies though.

Share this post


Link to post
40 minutes ago, dummzeuch said:

That name is based on the Seek function for a file of record. Usually the reader (we have many of these for various file types) is a thin wrapper around a file of record. Depending on the file type that "thinness" varies though.

Is there a ReadBuffer operation? If not, maybe create one that's a function returning True if it read enough data and False otherwise.

 

This way you could use a While loop to step through successive records rather than a for loop that keeps doing a seek each time just to go to the next record. The pointer should already be there, so why keep resetting it each time?

 

Also, it bothers me that your logic uses Exceptions when something isn't found, but maybe it's that serious if data is missing.

Share this post


Link to post
program StringListHelper;

{$APPTYPE CONSOLE}

uses
  Classes,
  System.SysUtils;

type
  TStringListItem = record
    str: string;
    obj: TObject;
  end;

  TStringListPairs = record
  private
    List: TStringList;
  type
    TEnumerator = record
    private
      List: TStringList;
      Index: Integer;
      function GetCurrent: TStringListItem;
    public
      function MoveNext: Boolean;
      property Current: TStringListItem read GetCurrent;
    end;

    TStringListAccess = class(TStrings)
    private
      FList: TStringItemList;
    end;
  public
    function GetEnumerator: TEnumerator;
  end;

  TStringListHelper = class helper for TStringList
  public
    function Pairs: TStringListPairs; inline;
  end;

{ TStringListPairs.TEnumerator }

function TStringListPairs.TEnumerator.GetCurrent: TStringListItem;
begin
  Result := TStringListItem(TStringListAccess(List).FList[Index]);
end;

function TStringListPairs.TEnumerator.MoveNext: Boolean;
begin
  Inc(Index);
  Result := Index < List.Count;
end;

{ TStringListHelper }

function TStringListHelper.Pairs: TStringListPairs;
begin
  Result.List := Self;
end;

{ TStringListPairs }

function TStringListPairs.GetEnumerator: TEnumerator;
begin
  Result.List := List;
  Result.Index := -1;
end;

var
  sl: TStringList;
begin
  sl := TStringList.Create;
  for var p in sl.Pairs do
    if p.str = 'foo' then
      Writeln(p.obj.ClassName);
end.

 

  • Like 1

Share this post


Link to post
47 minutes ago, David Schwartz said:

This way you could use a While loop to step through successive records rather than a for loop that keeps doing a seek each time just to go to the next record. The pointer should already be there, so why keep resetting it each time?

Not sure I understand what you mean here. Which pointer are you talking about? And why do you think it's being reset?

Depending on how the reader is implemented Seek would read a record from a file (already with a buffered stream) or from an internal data structure. In this particular case it just accesses data already read into an array.

 

52 minutes ago, David Schwartz said:

it bothers me that your logic uses Exceptions when something isn't found, but maybe it's that serious if data is missing.

It is serious in this case. The data in that file (and those associated with it) would be unusable and processing it should be stopped.

  • Like 1

Share this post


Link to post
5 minutes ago, David Heffernan said:

@Stefan Glienke You could readily tweak this to work with TStrings rather than TStringList and avoid all that hacking at the internals. But it's nice to see you using records for enumerators!

I leave that as an exercise to anyone who needs it - I would not want to have it run through the virtual Get and GetObject functions for every iteration. As for "All that hacking"- it's only that one private field access. And even that could be done a little cleaner by using the helper with Self trick.

I always use records for enumerators except when I don't 😉

Edited by Stefan Glienke
  • Like 3

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×