Reputation: 41
I'm trying to extract a substring from a string which would be the substring in between 2 delimiters i.e it should be defined as follows:
substring: aString delimiter: aDelimiter
and, for an example, if i'll get this line:
substring: 'dddd#sss#dddd' delimiter: '#'
the function should return 'sss'.
this is what i've been trying, which didn't work:
substring: aString delimiter: aDelimiter
|index temp1 temp2 sz arr str|
arr := aString asArray.
sz := arr size.
index := arr lastIndexOf: aDelimiter.
temp1 := arr first: (sz - index +1).
index := temp1 lastIndexOf: aDelimiter.
sz :=temp1 size.
temp2 := temp1 first: (sz - index).
str := temp2 asString.
^str.
I don't know if it's worth mentioning but it's supposed to be a class method.
Upvotes: 4
Views: 4022
Reputation: 14858
While I like both of the answers above, you might also want to consider another that is closer to your initial attempt and is a little bit more efficient than the others in that it only creates the object you are looking for (e.g., no intermediate Stream)
substringOf: aString delimitedBy: aCharacter
| i j |
i := aString indexOf: aCharacter.
j := aString indexOf: aCharacter startingAt: i + 1.
^aString copyFrom: i + 1 to: j - 1
(Note b.t.w. that I'm also suggesting a slightly different selector.)
Another aspect you would like to consider is how the method should react if aCharacter
is not in aString
, it is only once or it has three or more occurrences. Something in the lines of:
substringOf: aString delimitedBy: aCharacter
| i j |
i := aString indexOf: aCharacter.
i = 0 ifTrue: [^''].
j := aString indexOf: aCharacter startingAt: i + 1.
j = 0 ifTrue: [^''].
^aString copyFrom: i + 1 to: j - 1
But again, if performance is not a concern in your case, then go for the readStream upTo upTo answer, as it is probably the best.
Upvotes: 2
Reputation: 6390
You aren't far from working code, as David pointed out. But I'd just like to point out that it's very procedural. A lot of the magic of Smalltalk, and OOP in general, is writing beautiful, easy to understand code that sends intention revealing messages to a community of appropriate objects. This includes leaning on the objects already existing in the image. I can't think of a time when I've had to go this low level for a simple task like this. It would be great to read one of the many awesome OOP references. My favorite is A Mentoring Course on Smallalk
I think David's solution is right on. I personally like second
instead of at: 2
, but it feels picky and might be personal preference ('dddd#sss#dddd' subStrings: '#') second
Upvotes: 3
Reputation: 2847
Your basic problem is that the argument aDelimiter is a string instead of a character. You want to call it with $# instead of '#'.
Now for some easier ways. Probably the easiest is to use the subStrings: method:
('dddd#sss#dddd' subStrings: '#') at: 2
This has the disadvantage that it extracts the entire string into substrings separated by the # character which may be more than you need.
The next easiest option is to use streams:
'dddd#sss#dddd' readStream upTo: $#; upTo: $#
That code only extracts the part that you need.
Upvotes: 4