Skip to content

Commit f218b14

Browse files
committed
Add the helper function sequence_join to join a sequence by a separator
1 parent b97575d commit f218b14

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

pygmt/helpers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
is_nonstr_iter,
2424
launch_external_viewer,
2525
non_ascii_to_octal,
26+
sequence_join,
2627
)
2728
from pygmt.helpers.validators import validate_output_table_type

pygmt/helpers/utils.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,3 +711,110 @@ def args_in_kwargs(args: Sequence[str], kwargs: dict[str, Any]) -> bool:
711711
return any(
712712
kwargs.get(arg) is not None and kwargs.get(arg) is not False for arg in args
713713
)
714+
715+
716+
def sequence_join(
717+
value: Any,
718+
separator: str = "/",
719+
size: int | Sequence[int] | None = None,
720+
ndim: int = 1,
721+
name: str | None = None,
722+
) -> str | list[str] | None | Any:
723+
"""
724+
Join a sequence of values into a string separated by a separator.
725+
726+
A 1-D sequence will be joined into a single string. A 2-D sequence will be joined
727+
into a list of strings. Non-sequence values will be returned as is.
728+
729+
Parameters
730+
----------
731+
value
732+
The 1-D or 2-D sequence of values to join.
733+
separator
734+
The separator to join the values.
735+
size
736+
Expected size of the 1-D sequence. It can be either an integer or a sequence of
737+
integers. If an integer, it is the expected size of the 1-D sequence. If it is a
738+
sequence, it is the allowed sizes of the 1-D sequence.
739+
ndim
740+
The expected maximum number of dimensions of the sequence.
741+
name
742+
The name of the parameter to be used in the error message.
743+
744+
Returns
745+
-------
746+
joined_value
747+
The joined string or list of strings.
748+
749+
Examples
750+
--------
751+
>>> sequence_join("abc")
752+
'abc'
753+
>>> sequence_join(None)
754+
>>> sequence_join([])
755+
[]
756+
757+
>>> sequence_join([1, 2, 3, 4])
758+
'1/2/3/4'
759+
>>> sequence_join([1, 2, 3, 4], separator=",")
760+
'1,2,3,4'
761+
>>> sequence_join([1, 2, 3, 4], separator="/", size=4)
762+
'1/2/3/4'
763+
>>> sequence_join([1, 2, 3, 4], separator="/", size=[2, 4])
764+
'1/2/3/4'
765+
>>> sequence_join([1, 2, 3, 4], separator="/", size=[2, 4], ndim=2)
766+
'1/2/3/4'
767+
>>> sequence_join([1, 2, 3, 4], separator="/", size=2)
768+
Traceback (most recent call last):
769+
...
770+
pygmt.exceptions.GMTInvalidInput: Expected a sequence of 2 values, but got 4 values.
771+
>>> sequence_join([1, 2, 3, 4, 5], separator="/", size=[2, 4], name="parname")
772+
Traceback (most recent call last):
773+
...
774+
pygmt.exceptions.GMTInvalidInput: Parameter 'parname': Expected ...
775+
776+
>>> sequence_join([[1, 2], [3, 4]], separator="/")
777+
Traceback (most recent call last):
778+
...
779+
pygmt.exceptions.GMTInvalidInput: Expected a 1-D ..., but a 2-D sequence is given.
780+
>>> sequence_join([[1, 2], [3, 4]], separator="/", ndim=2)
781+
['1/2', '3/4']
782+
>>> sequence_join([[1, 2], [3, 4]], separator="/", size=2, ndim=2)
783+
['1/2', '3/4']
784+
>>> sequence_join([[1, 2], [3, 4]], separator="/", size=4, ndim=2)
785+
Traceback (most recent call last):
786+
...
787+
pygmt.exceptions.GMTInvalidInput: Expected a sequence of 4 values.
788+
>>> sequence_join([[1, 2], [3, 4]], separator="/", size=[2, 4], ndim=2)
789+
['1/2', '3/4']
790+
"""
791+
# Return the original value if it is not a sequence (e.g., None or str) or empty.
792+
if not is_nonstr_iter(value) or len(value) == 0:
793+
return value
794+
795+
# Change size to a list to simplify the checks.
796+
size = [size] if isinstance(size, int) else size
797+
798+
errmsg = {
799+
"name": f"Parameter '{name}': " if name else "",
800+
"sizes": ", ".join(str(s) for s in size) if size is not None else "",
801+
}
802+
803+
# Now it must be a sequence.
804+
if not is_nonstr_iter(value[0]): # 1-D sequence.
805+
if size is not None and len(value) not in size:
806+
msg = (
807+
f"{errmsg['name']}Expected a sequence of {errmsg['sizes']} values, "
808+
f"but got {len(value)} values."
809+
)
810+
raise GMTInvalidInput(msg)
811+
return separator.join(str(v) for v in value)
812+
813+
# Now it must be a 2-D sequence.
814+
if ndim == 1:
815+
msg = f"{errmsg['name']}Expected a 1-D sequence, but a 2-D sequence is given."
816+
raise GMTInvalidInput(msg)
817+
if size is not None and any(len(i) not in size for i in value):
818+
msg = f"{errmsg['name']}Expected a sequence of {errmsg['sizes']} values."
819+
raise GMTInvalidInput(msg)
820+
return [separator.join(str(j) for j in value[i]) for i in range(len(value))]

0 commit comments

Comments
 (0)