22
33from typing import TYPE_CHECKING
44
5- from narwhals ._duckdb .utils import F , lit
5+ from narwhals ._duckdb .utils import F , col , concat_str , lit
66from narwhals ._sql .expr_str import SQLExprStringNamespace
7- from narwhals ._utils import not_implemented
7+ from narwhals ._utils import not_implemented , requires
88
99if TYPE_CHECKING :
10+ from duckdb import Expression
11+
1012 from narwhals ._duckdb .expr import DuckDBExpr
1113
1214
@@ -27,4 +29,25 @@ def to_date(self, format: str | None) -> DuckDBExpr:
2729 compliant_expr = self .compliant
2830 return compliant_expr .cast (compliant_expr ._version .dtypes .Date ())
2931
32+ @requires .backend_version ((1 , 2 ))
33+ def to_titlecase (self ) -> DuckDBExpr :
34+ from narwhals ._duckdb .utils import lambda_expr
35+
36+ def _to_titlecase (expr : Expression ) -> Expression :
37+ extract_expr = F (
38+ "regexp_extract_all" , F ("lower" , expr ), lit (r"[a-z0-9]*[^a-z0-9]*" )
39+ )
40+ elem = col ("_" )
41+ capitalize = lambda_expr (
42+ elem ,
43+ concat_str (
44+ F ("upper" , F ("array_extract" , elem , lit (1 ))),
45+ F ("substring" , elem , lit (2 )),
46+ ),
47+ )
48+ capitalized_expr = F ("list_transform" , extract_expr , capitalize )
49+ return F ("list_aggregate" , capitalized_expr , lit ("string_agg" ), lit ("" ))
50+
51+ return self .compliant ._with_elementwise (_to_titlecase )
52+
3053 replace = not_implemented ()
0 commit comments