map apply and enumerate

This commit is contained in:
Jesse Bannon 2023-11-30 08:55:48 -08:00
parent f9545a7e03
commit df09fd5a99
4 changed files with 78 additions and 2 deletions

View file

@ -1,9 +1,14 @@
from typing import Optional from typing import Optional
from ytdl_sub.script.types.array import Array
from ytdl_sub.script.types.array import ResolvedArray
from ytdl_sub.script.types.map import Map from ytdl_sub.script.types.map import Map
from ytdl_sub.script.types.resolvable import AnyArgument from ytdl_sub.script.types.resolvable import AnyArgument
from ytdl_sub.script.types.resolvable import Boolean from ytdl_sub.script.types.resolvable import Boolean
from ytdl_sub.script.types.resolvable import Hashable from ytdl_sub.script.types.resolvable import Hashable
from ytdl_sub.script.types.resolvable import Integer
from ytdl_sub.script.types.resolvable import Lambda2
from ytdl_sub.script.types.resolvable import Lambda3
from ytdl_sub.script.utils.exceptions import KeyDoesNotExistRuntimeException from ytdl_sub.script.utils.exceptions import KeyDoesNotExistRuntimeException
@ -29,3 +34,26 @@ class MapFunctions:
Returns True if the key is in the Map. False otherwise. Returns True if the key is in the Map. False otherwise.
""" """
return Boolean(key in mapping.value) return Boolean(key in mapping.value)
# pylint: disable=unused-argument
@staticmethod
def map_apply(mapping: Map, lambda_function: Lambda2) -> Array:
"""
Apply a lambda function on the Map, where each arg
passed to the lambda function is ``key, value`` as two separate args.
"""
return ResolvedArray([ResolvedArray([key, value]) for key, value in mapping.value.items()])
@staticmethod
def map_enumerate(mapping: Map, lambda_function: Lambda3) -> Array:
"""
Apply a lambda function on the Map, where each arg
passed to the lambda function is ``idx, key, value`` as three separate args.
"""
return ResolvedArray(
[
ResolvedArray([Integer(idx), key_value[0], key_value[1]])
for idx, key_value in enumerate(mapping.value.items())
]
)

View file

@ -176,3 +176,14 @@ class Lambda2(Lambda):
@classmethod @classmethod
def num_input_args(cls) -> int: def num_input_args(cls) -> int:
return 2 return 2
@dataclass(frozen=True)
class Lambda3(Lambda):
"""
Type-hinting for functions that apply lambdas with three inputs per element
"""
@classmethod
def num_input_args(cls) -> int:
return 3

View file

@ -5,6 +5,7 @@ from typing import Callable
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Type from typing import Type
from typing import TypeVar
from typing import Union from typing import Union
from typing import get_origin from typing import get_origin
@ -13,6 +14,7 @@ from ytdl_sub.script.types.resolvable import BuiltInFunctionType
from ytdl_sub.script.types.resolvable import FunctionType from ytdl_sub.script.types.resolvable import FunctionType
from ytdl_sub.script.types.resolvable import Lambda from ytdl_sub.script.types.resolvable import Lambda
from ytdl_sub.script.types.resolvable import Lambda2 from ytdl_sub.script.types.resolvable import Lambda2
from ytdl_sub.script.types.resolvable import Lambda3
from ytdl_sub.script.types.resolvable import NamedType from ytdl_sub.script.types.resolvable import NamedType
from ytdl_sub.script.types.resolvable import Resolvable from ytdl_sub.script.types.resolvable import Resolvable
from ytdl_sub.script.types.variable import Variable from ytdl_sub.script.types.variable import Variable
@ -21,6 +23,9 @@ from ytdl_sub.script.utils.exceptions import UNREACHABLE
# pylint: disable=missing-raises-doc # pylint: disable=missing-raises-doc
TLambda = TypeVar("TLambda", bound=Lambda)
def is_union(arg_type: Type) -> bool: def is_union(arg_type: Type) -> bool:
""" """
Returns Returns
@ -156,10 +161,12 @@ class FunctionSpec:
return 0 # varargs can take any number return 0 # varargs can take any number
@property @property
def is_lambda_function(self) -> Optional[Type[Lambda | Lambda2]]: def is_lambda_function(self) -> Optional[Type[TLambda]]:
if Lambda3 in (self.args or []):
return Lambda3
if Lambda2 in (self.args or []): if Lambda2 in (self.args or []):
return Lambda2 return Lambda2
elif Lambda in (self.args or []): if Lambda in (self.args or []):
return Lambda return Lambda
return None return None

View file

@ -68,3 +68,33 @@ class TestMapFunctions:
.native .native
) )
assert output == expected_value assert output == expected_value
def test_map_apply(self):
output = (
Script(
{
"%custom_func": "{[%upper($0), %lower($1)]}",
"map1": "{{'Key1': 'Value1', 'Key2': 'Value2'}}",
"output": "{%map_apply(map1, %custom_func)}",
}
)
.resolve(update=True)
.get("output")
.native
)
assert output == [["KEY1", "value1"], ["KEY2", "value2"]]
def test_map_enumerate(self):
output = (
Script(
{
"%custom_func": "{[$0, %upper($1), %lower($2)]}",
"map1": "{{'Key1': 'Value1', 'Key2': 'Value2'}}",
"output": "{%map_enumerate(map1, %custom_func)}",
}
)
.resolve(update=True)
.get("output")
.native
)
assert output == [[0, "KEY1", "value1"], [1, "KEY2", "value2"]]