Jump to content
David Schwartz

fun coding challenge

Recommended Posts

Just for fun, I thought I'd post this here.

 

I have four TMemos on a form into which users can enter some lines of text, usually not more than 5 lines, but that's just a guideline. Let's call them m1, m2, m3, and m4.

 

There's a field where they can specify a "delimiter" like a space, comma, hyphen, underscore, ampersand, etc.

 

Write a method that creates a list of strings in an output TMemo composed of all possible combinations of lines from each of the four TMemos left-to-right (1+2+3+4) with the "delimiter" inserted between each one.

 

So you get something like

rsltmemo.Add( m1.Lines[i]+delim+m2.Lines[j]+delim+m3.Lines[k]+delim+m4.Lines[n] );

where each Lines index is in the range [0..Lines.Count-1] for that memo.

 

For me, the obvious approach was four nested loops.

 

Then someone suggested, "Why not just have an [+Add] button that lets you add as many of these boxes [ie., input TMemos] as you want?"

 

The challenge is to write a method to generate the output from some 'n' input TMemos in as few statements as possible?

 

Right off the bat, I can think of at least four different ways to do it, and more if there are things in the VCL libs that might help that I'm not aware of.

 

(I have a feeling a good functional programming lib would let you do it in just a few statements for an arbitrary number of input TMemos.)

 

Edited by David Schwartz

Share this post


Link to post

For one memo the result is just that memo. Given the result for n memos, the result for adding another memo is just the combination of that result with the new memo. 

procedure Combine(Source, Target: TStrings; const Delimiter: string = ';');
var
  lst: TStringList;
  S: string;
  T: string;
begin
  if Target.Count = 0 then begin
    Target.Assign(Source);
  end
  else begin
    lst := TStringList.Create();
    try
      for S in Source do
        for T in Target do
          lst.Add(T + Delimiter + S);
      Target.Assign(lst);
    finally
      lst.Free;
    end;
  end;
end;

The calling part could look like this:

var
  A: TStringList;
  memo: TMemo;
begin
  A := TStringList.Create;
  try
    for memo in [memo1, memo2, memo3] do
      Combine(memo.Lines, A);
    memResult.Lines := A;
  finally
    A.Free;
  end;
end;

 

  • Thanks 1

Share this post


Link to post

Just store the TMemos in a TList when you create them so you don't have to find them later. When generating; have two nested cycles going through that list. Outer from Low(list) To High(list) - 1; inner from outer var to High(list). Not efficient, but easy enough to understand what is going on.

  • Like 1

Share this post


Link to post
Guest
On 10/18/2020 at 11:38 PM, David Schwartz said:

Just for fun, I thought I'd post this here.

...

(I have a feeling a good functional programming lib would let you do it in just a few statements for an arbitrary number of input TMemos.)

 

Maybe some like this:

image.thumb.png.07f8567a2dfcea0acbd4018022831d63.png   image.thumb.png.76019ab5eb45322a90f89ae98ce7a38d.png

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
  lDelimiter: string = '#';

procedure TForm1.Button2Click(Sender: TObject);
begin
  prcJoinMemosLinesWithDelimiter([Memo1, Memo2, Memo3, Memo4], 2);
end;

procedure TForm1.prcJoinMemosLinesWithDelimiter(lMemos: array of TMemo; lOnlyNLines: integer);
var
  lLine  : integer;
  lMyMemo: TMemo;
begin
  for lMyMemo in lMemos do
  begin
    for lLine := 0 to Pred(lOnlyNLines) do
      Memo5.Lines.Add(lMyMemo.Lines[lLine]); // or ..Lines[lLine] + lDelimiter ); for each line // using "Add()" because the "lOnlyNLines" context
    //
    // Memo5.Lines.Add(lDelimiter);
  end;
end;

 

Edited by Guest

Share this post


Link to post
procedure CombineMemos(const AMemos: array of TMemo;
  ADelimiter: Char; AResult: TMemo);
var
  i, cnt: Integer;
  rem:    UInt64;
  idx:    UInt64;
  mm:     TMemo;
  s:      string;
begin
  cnt := Ord(Length(AMemos) > 0);
  for mm in AMemos do
    cnt := cnt * mm.Lines.Count;
  for i := 0 to cnt - 1 do
  begin
    s   := '';
    rem := i;
    for mm in AMemos do
    begin
      DivMod(rem, mm.Lines.Count, rem, idx);
      s := s + IfThen(mm <> AMemos[0], ADelimiter, '') + mm.Lines[idx];
    end;
    AResult.Lines.Add(s);
  end;
end;

 

Edited by balabuev

Share this post


Link to post
Guest
5 hours ago, balabuev said:

procedure CombineMemos(const AMemos: array of TMemo;
  ADelimiter: Char; AResult: TMemo);
var
...
begin
...
   s := s + IfThen(mm <> AMemos[0], ADelimiter, '') + mm.Lines[idx];
...
end;

 

This line, in RAD 10.3.3 and RAD 10.4.1 show an error on "Params" of IfThen() function

And, if any Memo contain 1 line more than others, the content will be repeated on final resulted until all than all memo having same number lines!!!

 

image.thumb.png.e9bee1e5cd16fffd448f0db9bcdc7831.pngimage.thumb.png.e412c86543a55bdcc854a09c5eee3ba7.png    

Share this post


Link to post

Everything works ok. You have to use Math and StrUtils units.

 

image.thumb.png.8676e915e5b2051ebe58abe595618f20.png

Edited by balabuev

Share this post


Link to post
Guest

if was said before... about "StrUtils". I used just "Math".

ok I'll try later.

thanks

Edited by Guest

Share this post


Link to post
Guest
On 11/16/2020 at 3:14 AM, balabuev said:

Everything works ok. You have to use Math and StrUtils units.

...

MATH and STRSYSTEM units added, but the same resulted:

 

image.thumb.png.b556eb55aebb1ac0b914a888561a132f.png

Edited by Guest

Share this post


Link to post
Guest

maybe this will be better than my first sample:

 

image.thumb.png.1618f0e6010dcd713b010ee993d88784.png

function TForm1.fncJoinMemosLinesWithDelimiter(lMemos: array of TMemo; lOnlyNLines: integer; lDelimiter: Char = '#'): string;
var
  lMyMemo  : TMemo;
  lEachLine: string;
begin
  for lMyMemo in lMemos do
  begin
    for lEachLine in lMyMemo.Lines do
    begin
      if ((lMyMemo.Lines.IndexOf(lEachLine) - 1) > lOnlyNLines) then
        break;
      //
      result := result + lEachLine + lDelimiter;
    end;
  end;
end;

procedure TForm1.btnMyCodeClick(Sender: TObject);
begin
  Memo5.Lines.Add(                                                  { }
    fncJoinMemosLinesWithDelimiter([Memo1, Memo2, Memo3, Memo4], 2) { }
    );
end;

 

hug

Edited by Guest

Share this post


Link to post

I think you misunderstood the initial task...

On 10/19/2020 at 5:38 AM, David Schwartz said:

Write a method that creates a list of strings in an output TMemo composed of all possible combinations of lines from each of the four TMemos left-to-right (1+2+3+4) with the "delimiter" inserted between each one.

 

Edited by balabuev

Share this post


Link to post
Guest
1 hour ago, balabuev said:

I think you misunderstood the initial task...

 

you can be right! sorry my point!

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

×