# Custom functions Custom functions include both **custom expressions** and **custom transforms**. Both capabilities are useful for repeating logic across your pipeline while managing it in a single location. In the Pipeline Builder interface, these are accessed separately as **Custom Expressions** and **Custom Transform Path** under the **Reusables** dropdown. **Custom transforms** enable you to save a series of transform boards as a single transform for reuse across your pipeline. **Custom expressions** enable you to save a single expression with optional parameters for reuse across your pipeline. Custom expressions can be used inside of other transforms such as filters or case statements. A custom function generally consists of a **name** (required), **description** (optional), **function arguments** (optional), and the **function definition** (required). ## Create a custom expression To create a custom expression, select the **Reusables** dropdown at the top of the graph, then choose **Custom Expressions**. ![The custom expression option in the reusables drop down.](../../../assets/9a6b33832803fc384c73c6249567dc78176b4ec8eccb50745623680182f4246c.webp) Select **Add custom expression**. ![An empty custom functions window.](../../../assets/352a02213dd19596d9081fba49e6986477cb37c0a2ac48ed5553e23ebcc3cdeb.webp) In the new window, configure your custom expression. Enter a name in the top left, add any arguments in the **Arguments** window using the **Add argument** option, and define your expression. Select **Apply** when finished. ![The custom expression configuration window.](../../../assets/239e7e14c0b823dce078361908767d075358391f2c8220691f03c2ac644608d0.webp) After creating a custom expression, you can search for it the same as any other expression in a transform node or in a transform. ## Create a custom transform Two steps are required to create a custom transform: 1. Define your logic as a series of transform boards. 2. Convert the transform boards into a new custom function. Below, we will review an example for each step. ### Define a series of transform boards Suppose you have a table of `users`, and you want to create a primary key to uniquely identify each user. You know that each user’s `first_name`, `last_name`, and `first_login_date` is a unique combination. You would like to add a column `primary_key` of type `String` to the dataset, which is a hash of those three columns, then drop `first_name`, `last_name`, and `first_login_date`. Finally, you want to keep only one row per user if there are duplicates, retaining the row with the lowest `age` value. ![Screenshot of input table with users](../../../assets/903eee3603e56c677fc33ed3b3f86cedb7627673c6b9cc6bcc7810e32a836189.webp) First, combine `first_name`, `last_name`, and `first_login_date` into one column. You can use the `Concatenate strings` transform to add those three columns together. ![Screenshot of concatenate strings transform](../../../assets/48598b2af7e196b71fa33fc5b11c21b8264d132cd6c6de009875b8b4d54cc6e6.webp) For the `Separator` field, enter a `-`. Then, choose each column in the **Expressions** dropdown. For the first two columns, choose `first_name` and `last_name`. However, the `first_login_date` column is not an option for us to choose from for our third field. This is because it is a `Date` type, and the `Concatenate strings` function only accepts `String` types. ![Screenshot of concatenate strings transform without date column](../../../assets/45f9bea3ff047780e8fe065e729e7a69120e826579b3cd504b21a0281686b3cf.webp) To resolve this, insert a `Cast` expression from the **Expressions** tab. The parameters will be `first_login_date` for the `Expression` and `String` for the `Type` to cast it to. This prevents you from needing to change `first_login_date` globally, which would affect all downstream transform boards. ![Screenshot of concatenate strings transform with cast expression](../../../assets/aaac8d0a1fc4f9cec2ae32356b6ef5a6c2ed4c6df0a88aaa875aa57088d8a49c.webp) Once you select `Apply`, the output table should look like this: ![Screenshot of output table after concatenate strings transform](../../../assets/2b1010a050b026212932a653439acaf4b08f9210cfab9124907999c1091f0abf.webp) Now, you must de-identify the data within `primary_key`. One way to do this is to create a hash for each value in `primary_key` by applying the `Hash sha256` transform. You can do this in the same board by selecting the `Reuse value` option to replace `Concatenate strings` with `Hash sha256`. ![Screenshot of reuse value with hash sha256 transform](../../../assets/ce3d6e2e1e6569a6ae91f779d1d4c3a0a928bf9e412261719248ba34953c476c.webp) This option keeps the existing values and makes them the first input to the new transform. ![Screenshot of hash sha256 transform](../../../assets/22c6358463b7deaa6554b0f014638ef829eb6eb2bc2855685383b63b5f00808e.webp) After selecting `Apply`, validate that your output matches what you would expect: a `primary_key: String` column that contains unique data about each row. ![Screenshot of intermediate output table preview](../../../assets/43278cd1e39b65f38eba642286c43dfb6251f4e84d6e9e13364cefced0b9036a.webp) Now, you will notice that the first and last row in `primary_key` are the same. You want to keep the row with the lower `age` of 25, and drop `first_name`, `last_name`, and `first_login_date`. To do this, add an `Aggregate` transform. Input `primary_key` in the first field `Group by columns`, and `age` in the second field `Aggregations` with a `Min` expression: ![Screenshot of aggregate transform](../../../assets/3468dc55323999cae5f575dd2cdeccb030ca236dda65fc8b8795ae23ed5d0f51.webp) Finally, the output table should look like the image below. You can verify that there is only one row for `primary_key = b3c01...` and `age` is 25. ![Screenshot of final output table preview](../../../assets/e7b39fa4e1d171e24f1810c6f7f8569c389af6929f6c0a0ef153b28085205c9b.webp) ### Convert a series of transform boards into a custom function Now, suppose that you want to reuse this logic in three different places within your pipeline. We can convert our logic into a custom function by using `Shift + Down Arrow` to select both boards, then selecting the **+** button in the top bar. ![Screenshot of converting multiple transform boards to custom function](../../../assets/a763ac73921bde48d4748867b679f24398eec6cdad1ba57baa5df3dbbf8d82a8.webp) This will take you to the custom function creation page. You will notice that the column inputs `first_name`, `last_name`, `first_login_date`, and `age` are crossed out; this is because the function you are creating will be generic for any four inputs of the correct types within your pipeline regardless of column name. ![Screenshot of custom function creation page](../../../assets/13047b49922cdd97115c518fb37071017d300c8310a512a2033f038115afbc4d.webp) To define these inputs, select **Add argument** four times. Configure the argument names by clicking into the yellow box. ![Screenshot of custom function arguments initial configuration](../../../assets/02848aebdc3381e8b8a0b3b510bad215a3ebe2b3440191cd29822108d4c9c01c.webp) Then, configure the argument types by clicking into the gear icon to the right of each argument. Here, you want the first two arguments to be type `Expression` to represent columns of `String` type, the third argument to be type `Expression`, and the last column to be type `Expression`. ![Screenshot of custom function arguments complete configuration](../../../assets/31151d673892c57e941cde3ac2d23fb560547c91989af53098d2067392560737.webp) Now, you can add your new arguments to the two boards as before. However, they will now be available in the `Parameters` section instead of the `Columns` section. ![Screenshot of custom function with arguments](../../../assets/f023aac4511e8f0079e2d6e9203b3b5a4f79439bc19a1817d16a66270f78c811.webp) Once you have finished your configuration, name the function `Primary key generator`, give it an optional description, and select **Apply all changes**. ![Screenshot of custom function arguments finalized configuration](../../../assets/2ac02d3fb75ea387502e70313ad16e4942d4c8484edb00be570f5e0dd5b05de8.webp) The new function is now available for use in any transform path. The next time you need to create a primary key with this pattern, you can search `Primary key generator` in the transform dropdown. ![Screenshot of custom function available in search bar](../../../assets/934e4688ab2bb1fa77ebcfb4c273b169509989c9a5e358950246d93c12daf230.webp) You can populate your custom function with the original columns: `first_name`, `last_name`, `first_login_date`, and `age`. ![Screenshot of custom function configuration within transform path](../../../assets/95a42ec5dab285574777b8f214213ce1f0af68727934b503e8ce220a85178b5e.webp) By populating the relevant column names into your custom function, you are producing the same result as if you had created the function with the same transforms in the path. However, by creating the custom function, you can save the function logic to reuse across your pipeline rather than recreating logic with separate transform boards. ![Screenshot of final output table preview](../../../assets/e7b39fa4e1d171e24f1810c6f7f8569c389af6929f6c0a0ef153b28085205c9b.webp)