Page 1 of 2
ForEach for Strings
Posted: Tue Jul 16, 2024 3:06 pm
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.
Re: ForEach for Strings
Posted: Tue Jul 16, 2024 3:24 pm
by Quin
+1
String indexing like arrays would also be incredibly handy, but that may be a topic for a different day.
Re: ForEach for Strings
Posted: Tue Jul 16, 2024 4:46 pm
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
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 2:02 am
by BarryG
(Not what OP requested).
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 4:14 am
by AZJIO
everything has already been invented a long time ago
SplitL
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 5:12 am
by jacdelad
That's not the point and not the same as my feature request.
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 5:15 am
by BarryG
(Not what OP requested).
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 5:17 am
by jacdelad
No, I want an extended ForEach.
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 5:25 am
by BarryG
Okay, never mind. Sorry! I thought I was onto something for you. My bad.
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 5:51 am
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
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 6:30 am
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
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 8:30 am
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.
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 3:30 pm
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.
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 4:55 pm
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.
Re: ForEach for Strings
Posted: Wed Jul 17, 2024 6:30 pm
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()