ForEach for Strings

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

ForEach for Strings

Post by jacdelad »

Hi,
I don't know if somebody else agrees, but I would really appreciate to have a ForEach to be used with strings/substrings:

Code: Select all

MyString$="1|2|3|4|5|..."
Counter=CountString(MyString$,"|")+1
For Count=1 To Counter
  Temp$=StringField(MyString$,Count,"|")
  ;Do stuff with Temp$
Next
could be done shorter, more readable and more efficient as

Code: Select all

MyString$="1|2|3|4|5|..."
ForEach MyString$,"|",Temp$
  ;Do stuff with Temp$
Next
or

Code: Select all

MyString$="1|2|3|4|5|..."
ForEach MyString$,"|",@Temp$
  ;Do stuff with Temp$
Next
Since there are several scenarios which require all substrings of a string, this could come in handy.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Quin
Addict
Addict
Posts: 1131
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: ForEach for Strings

Post by Quin »

+1
String indexing like arrays would also be incredibly handy, but that may be a topic for a different day.
User avatar
NicTheQuick
Addict
Addict
Posts: 1504
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: ForEach for Strings

Post by NicTheQuick »

Quin wrote: Tue Jul 16, 2024 3:24 pm String indexing like arrays would also be incredibly handy, but that may be a topic for a different day.
You at least can do that with some pointer magic:

Code: Select all

Structure CharArray
	c.c[0]
EndStructure

Define mystring.s = "Hello World"

Define *mystring.CharArray = @mystring
*mystring\c[0] = 'h'

Debug mystring
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
BarryG
Addict
Addict
Posts: 4136
Joined: Thu Apr 18, 2019 8:17 am

Re: ForEach for Strings

Post by BarryG »

(Not what OP requested).
Last edited by BarryG on Wed Jul 17, 2024 5:24 am, edited 1 time in total.
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: ForEach for Strings

Post by AZJIO »

everything has already been invented a long time ago
SplitL
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: ForEach for Strings

Post by jacdelad »

That's not the point and not the same as my feature request.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
BarryG
Addict
Addict
Posts: 4136
Joined: Thu Apr 18, 2019 8:17 am

Re: ForEach for Strings

Post by BarryG »

(Not what OP requested).
Last edited by BarryG on Wed Jul 17, 2024 5:25 am, edited 3 times in total.
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: ForEach for Strings

Post by jacdelad »

No, I want an extended ForEach.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
BarryG
Addict
Addict
Posts: 4136
Joined: Thu Apr 18, 2019 8:17 am

Re: ForEach for Strings

Post by BarryG »

Okay, never mind. Sorry! I thought I was onto something for you. My bad.
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: ForEach for Strings

Post by AZJIO »

jacdelad wrote: Wed Jul 17, 2024 5:17 am No, I want an extended ForEach.
I think there is a common syntax used in many languages. An example of a custom syntax in PECMD that doesn't look like anything. It's like one person's own language.

By the way, if there are solutions using the existing syntax, then no one will come up with anything. But the function I proposed duplicates the memory by creating a list with the same contents. I tried to make pointers from the string and now the memory is not duplicated (probably).

Code: Select all

EnableExplicit

Procedure SplitL3(*c.Character, List StringList(), *jc.Character)
	Protected *t.Character = *c
	ClearList(StringList())
	While *c\c
		If *c\c = *jc\c
			*c\c = 0
			*c + SizeOf(Character)
			If *c\c
				AddElement(StringList())
				StringList() = *t
				*t = *c
			Else
				Break
			EndIf
		EndIf
		*c + SizeOf(Character)
	Wend
	AddElement(StringList())
	StringList() = *t
EndProcedure

Define St.s = "This is a test string to see if split and join are working."

NewList WordsList()
SplitL3(@St, WordsList(), @" ")

ForEach WordsList()
    Debug PeekS(WordsList())
Next
Last edited by AZJIO on Wed Jul 17, 2024 2:03 pm, edited 2 times in total.
User avatar
idle
Always Here
Always Here
Posts: 5840
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: ForEach for Strings

Post by idle »

AZJIO wrote: Wed Jul 17, 2024 5:51 am
jacdelad wrote: Wed Jul 17, 2024 5:17 am No, I want an extended ForEach.
I think there is a common syntax used in many languages. An example of a custom syntax in PECMD that doesn't look like anything. It's like one person's own language.

By the way, if there are solutions using the existing syntax, then no one will come up with anything. But the function I proposed duplicates the memory by creating a list with the same contents. I tried to make pointers from the string and now the memory is not duplicated (probably).

Code: Select all

EnableExplicit

Procedure SplitL3(*c.Character, List StringList(), *jc.Character)
	Protected *t.Character = *c
	ClearList(StringList())
	While *c\c
		If *c\c = *jc\c
			*c\c = 0
			*c + SizeOf(Character)
			AddElement(StringList())
			StringList() = *t
			*t = *c
		EndIf
		*c + SizeOf(Character)
	Wend
	AddElement(StringList())
	StringList() = *t
EndProcedure

Define St.s = "This is a test string to see if split and join are working."

NewList WordsList()
SplitL3(@St, WordsList(), @" ")

ForEach WordsList()
    Debug PeekS(WordsList())
Next
that's a very good solution
Mesa
Enthusiast
Enthusiast
Posts: 433
Joined: Fri Feb 24, 2012 10:19 am

Re: ForEach for Strings

Post by Mesa »

A macro can do a foreach:

Code: Select all

Macro ForEachS(S,T)
  For Count=1 To CountString(S,"|")+1
    T=StringField(S,Count,"|")
  EndMacro
  
  MyString$="1|2|3|4|5|..."
  ForEachS(MyString$,Temp$)
  Debug Temp$
Next
M.
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: ForEach for Strings

Post by jacdelad »

Ok, so without wanting to sound rude: I know how to do this with existing code elements, sure. If it was something outstanding, I or someone else would have posted it in Tipps&Tricks. This section is for wishes, so, as I see it, things that are either not doable at the moment or things that change/enhance PureBasic internally.

As I wrote in my initial post, this is nothing completely new. I also know how to do this with existing functions. Your ideas are great as well. BUT, my request/question, was and is to enhance the command "ForEach". By your logic we don't need ForEach at all. Going through a list doesn't even need an auxiliar variable. But the command is here and I use it regularly. Is just want to use it with strings too, because I regularly go through string parts. I don't care how it would be done internally, I just want it to work with ForEach. That's why it is a request, not a trick or a question. Period.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
tored
User
User
Posts: 86
Joined: Wed Feb 16, 2022 12:47 pm
Location: Sweden

Re: ForEach for Strings

Post by tored »

Would be great if possible to hook into ForEach with an iterator, that way we can use ForEach with whatever collections we have.
tored
User
User
Posts: 86
Joined: Wed Feb 16, 2022 12:47 pm
Location: Sweden

Re: ForEach for Strings

Post by tored »

tored wrote: Wed Jul 17, 2024 4:55 pm Would be great if possible to hook into ForEach with an iterator, that way we can use ForEach with whatever collections we have.
Proof of concept

Edit: Fixed incorrect current() for IntArrayIterator

Code: Select all

EnableExplicit

DeclareModule Iterator
  EnableExplicit
  
  Interface Iterator
    current()
    key()
    nxt()
    rewind()
    valid()
    free()
  EndInterface

  Macro Loop(it)
    it\rewind()
    While it\valid()
  EndMacro
  
  Macro EndLoop(it)
    it\nxt()
    Wend
  EndMacro
EndDeclareModule  

Module Iterator
  
EndModule

DeclareModule IntArrayIterator
  EnableExplicit
  
  Declare New(Array arr.i(1))
EndDeclareModule

Module IntArrayIterator
  
  Structure IntArray
    i.i[0]
  EndStructure
  
  Structure This
    *vt
    *arr.IntArray
    size.i
    pos.i
  EndStructure  
  
  Procedure Current(*this.This)
    ProcedureReturn @*this\arr\i[*this\pos]
  EndProcedure
  
  Procedure Key(*this.This)
    ProcedureReturn *this\pos
  EndProcedure  
  
  Procedure Nxt(*this.This)
    *this\pos + 1
  EndProcedure
  
  Procedure Rewind(*this.This)
    *this\pos = 0
  EndProcedure
  
  Procedure Valid(*this.This)
    ProcedureReturn Bool(*this\pos =< *this\size)
  EndProcedure 
  
  Procedure Free(*this.This)
    FreeMemory(*this)
  EndProcedure  
  
  DataSection
    vt:
    Data.i @Current()
    Data.i @Key()
    Data.i @Nxt()
    Data.i @Rewind()
    Data.i @Valid()
    Data.i @Free()
  EndDataSection
  
  Procedure New(Array arr.i(1))
    Protected *this.This
    *this = AllocateMemory(SizeOf(This))
    If *this
      *this\vt = ?vt
      *this\arr = @arr()
      *this\size = ArraySize(arr())
    EndIf  
    ProcedureReturn *this  
  EndProcedure
EndModule

DeclareModule SplitStringIterator
  EnableExplicit
  
  Declare New(*str, delim.s)
EndDeclareModule  

Module SplitStringIterator
  
  Structure This
    *vt
    *s.String
    size.i
    pos.i
    delim.s
    tmp.s
  EndStructure  
  
  Macro SetTmp(this)
    this\tmp = StringField(this\s\s, this\pos + 1, this\delim)
  EndMacro  
  
  Procedure Current(*this.This)
    ProcedureReturn @*this\tmp
  EndProcedure
  
  Procedure Key(*this.This)
    ProcedureReturn *this\pos
  EndProcedure  
  
  Procedure Nxt(*this.This)
    *this\pos + 1
    SetTmp(*this)
  EndProcedure
  
  Procedure Rewind(*this.This)
    *this\pos = 0
  EndProcedure
  
  Procedure Valid(*this.This)
    ProcedureReturn Bool(*this\pos =< *this\size)
  EndProcedure 
  
  Procedure Free(*this.This)
    FreeMemory(*this)
  EndProcedure  
  
  DataSection
    vt:
    Data.i @Current()
    Data.i @Key()
    Data.i @Nxt()
    Data.i @Rewind()
    Data.i @Valid()
    Data.i @Free()
  EndDataSection
  
  Procedure New(*str, delim.s)
    Protected *s.String = @*str
    Protected *this.This
    *this = AllocateMemory(SizeOf(This))
    If *this
      *this\vt = ?vt
      *this\s = *s
      *this\size = CountString(*s\s, delim)
      *this\delim = delim
      SetTmp(*this)
   EndIf  
    ProcedureReturn *this  
  EndProcedure
EndModule
  
UseModule Iterator

Dim arr(1)
arr(0) = 2
arr(1) = 17

Define *it.Iterator = IntArrayIterator::New(arr())

Loop(*it)
  Debug Str(*it\key()) + ":" + Str(PeekI(*it\current()))
EndLoop(*it)
*it\free()

Define s.s = "one;two;three"
*it = SplitStringIterator::New(@s, ";")

Loop(*it)
  Debug PeekS(*it\current())
EndLoop(*it)
*it\free()


Post Reply