Skip to content

Commit 2e4f532

Browse files
authored
Merge pull request #5 from pavanagrawal123/pavan/fix_replace
add test + fix replace function implementation
2 parents cda7eb6 + eef8940 commit 2e4f532

File tree

2 files changed

+80
-14
lines changed

2 files changed

+80
-14
lines changed

src/jsonata/functions.py

+74-14
Original file line numberDiff line numberDiff line change
@@ -755,22 +755,82 @@ def replace(string: Optional[str], pattern: Union[str, re.Pattern], replacement:
755755
if isinstance(pattern, str):
756756
if not pattern:
757757
raise jexception.JException("Second argument of replace function cannot be an empty string", 0)
758-
if limit is None:
759-
if isinstance(pattern, str):
760-
return re.sub(pattern, str(replacement), string)
761-
else:
762-
return Functions.safe_replace_all(string, pattern, replacement)
763-
else:
758+
759+
if limit is not None and limit < 0:
760+
raise jexception.JException("Fourth argument of replace function must evaluate to a positive number", 0)
761+
762+
def string_replacer(match):
763+
result = ''
764+
position = 0
765+
repl = str(replacement)
766+
while position < len(repl):
767+
index = repl.find('$', position)
768+
if index == -1:
769+
result += repl[position:]
770+
break
771+
result += repl[position:index]
772+
position = index + 1
773+
if position < len(repl):
774+
dollar_val = repl[position]
775+
if dollar_val == '$':
776+
result += '$'
777+
position += 1
778+
elif dollar_val == '0':
779+
result += match.group(0)
780+
position += 1
781+
else:
782+
max_digits = len(str(len(match.groups())))
783+
group_num = repl[position:position+max_digits]
784+
if group_num.isdigit():
785+
group_index = int(group_num)
786+
if 0 < group_index <= len(match.groups()):
787+
result += match.group(group_index) or ''
788+
position += len(group_num)
789+
else:
790+
result += '$'
791+
else:
792+
result += '$'
793+
else:
794+
result += '$'
795+
return result
764796

765-
if limit < 0:
766-
raise jexception.JException("Fourth argument of replace function must evaluate to a positive number", 0)
797+
if callable(replacement):
798+
replacer = lambda m: replacement(m.groupdict())
799+
elif isinstance(replacement, str):
800+
replacer = string_replacer
801+
else:
802+
replacer = lambda m: str(replacement)
767803

768-
for i in range(0, limit):
769-
if isinstance(pattern, str):
770-
string = re.sub(pattern, str(replacement), string, 1)
771-
else:
772-
string = Functions.safe_replace_first(string, pattern, str(replacement))
773-
return string
804+
if isinstance(pattern, str):
805+
# Use string methods for literal string patterns
806+
result = ''
807+
position = 0
808+
count = 0
809+
while True:
810+
if limit is not None and count >= limit:
811+
result += string[position:]
812+
break
813+
index = string.find(pattern, position)
814+
if index == -1:
815+
result += string[position:]
816+
break
817+
result += string[position:index]
818+
match = re.match(re.escape(pattern), string[index:])
819+
result += replacer(match)
820+
position = index + len(pattern)
821+
count += 1
822+
return result
823+
else:
824+
# Use regex for pattern objects
825+
if limit is None:
826+
return Functions.safe_replace_all(string, pattern, replacement)
827+
else:
828+
count = 0
829+
result = string
830+
while count < limit:
831+
result = Functions.safe_replace_first(result, pattern, str(replacement))
832+
count += 1
833+
return result
774834

775835
#
776836
# Base64 encode a string

tests/string_test.py

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ def test_escape(self):
3232
assert jsonata.Jsonata("$string($)").evaluate({"a": str('\n')}) == "{\"a\":\"\\n\"}"
3333
assert jsonata.Jsonata("$string($)").evaluate({"a": "</"}) == "{\"a\":\"</\"}"
3434

35+
def test_replace(self):
36+
assert jsonata.Jsonata("$replace('hello', '.', '')").evaluate(None) == "hello"
37+
assert jsonata.Jsonata("$replace('hello', 'l', 'x')").evaluate(None) == "hexxo"
38+
assert jsonata.Jsonata("$replace('h.ello', '.', '')").evaluate(None) == "hello"
39+
assert jsonata.Jsonata("$replace('h.e.l.l.o', '.', '',2)").evaluate(None) == "hel.l.o"
40+
3541
#
3642
# Additional $split tests
3743
#

0 commit comments

Comments
 (0)