# Post-mapping transformations (partner-defined schema)

In this article, you will go through some common data transformation challenges that you might run into while building integrations.

In this article, you will go through some common data transformation challenges that you might run into while building integrations.
For each example that follows:

* We will be using dummy data which you might get from another service.
* We will be showing possible pathways (Core connectors, JSONata, Javascript) for each example. You are free to take a different route as well.

## Salesforce

### 1 - Preparing input for Salesforce update operation

We have the following dummy data:

### Dummy data

```json
[
  \{
    "LastName": "Lauren",
    "Company": "Web Design, Inc.",
    "Email": "laurenbailey@ymail.com",
    "object_id": "00Q4J00000BIOq6UAH"
  \},
  \{
    "LastName": "Alexis",
    "Company": "Education Corp.",
    "Email": "awilson@hotmail.com",
    "object_id": "00Q4J00000BIVP1UAP"
  \}
]
```

Our Salesforce Connector's update operation expects the following input for the above data:

### Required input schema

```json
{
   "batch_update_list":[
      {
         "fields":[
            \{
               "value":"Lauren",
               "key":"LastName"
            \},
            \{
               "value":"Web Design, Inc.",
               "key":"Company"
            \},
            \{
               "value":"laurenbailey@ymail.com",
               "key":"Email"
            \}
         ],
         "object_id":"00Q4J00000BIOq6UAH"
      },
      {
         "fields":[
            \{
               "value":"Alexis",
               "key":"LastName"
            \},
            \{
               "value":"Web Design, Inc.",
               "key":"Company"
            \},
            \{
               "value":"laurenbailey@ymail.com",
               "key":"Email"
            \}
         ],
         "object_id":"00Q4J00000BIVP1UAP"
      }
   ]
}
```

> **Info:** Please refer '[Determining Input schema](https://tray.io/documentation/tray-uac/building-automations/transforming-data-for-input/satisfying-input-schema/)' page which shows how to find out input and output data format for services.

You can import this project [here](#). It contains three ways you could solve this:

### 1 - No Code Approach

![dataMapping1 noCode](https://tray.ai/documentation/images/platform/embedded/advanced-features/data-mapping/data-transformation/partner-defined-schema/1BhgtrwiEyXs4vp415gVLX_dataMapping1_noCode.png)
Here we loop through the input array and use 'Data Storage' connector's 'Append to List' operation to add each object to the key 'batch\_update\_list'.

### 2 - Using JSON transformer

![sfUpdateDataTransform jsonata](https://tray.ai/documentation/images/platform/embedded/advanced-features/data-mapping/data-transformation/partner-defined-schema/6SrmoewV8bDYakQrF7YPhp_sfUpdateDataTransform_jsonata.png)
Below is the JSONata code:

```js
(
/* prepare list without object_id field as this won't be mapped in fields array*/
  $listWithoutObjectID := ($ ~> |$|{}, ['object_id']|);
/* map new list without object_id */
  $map ($listWithoutObjectID, function($item, $index) { 
    $merge([
        {
            'fields' : $map($keys($item), function($v, $i){
                \{
                    'key' : $v,
                    'value' : $lookup($item, $v)
                \}
            })
        },
        {
            /* add object_id key separately at the end */
            'object_id': $[$index].object_id
        }
    ])
  })
)
```

Explanation:

1. We prepare a new list without `object_id` field as this won't be mapped in the **fields** array which will contain keys and values.
2. We map this new list by mapping keys and values.
3. Finally, we add the `object_id` field back.

### 3 - Using script

![sfUpdateDataTransform script](https://tray.ai/documentation/images/platform/embedded/advanced-features/data-mapping/data-transformation/partner-defined-schema/21mtsSBfmMjVLCo6KAEqRQ_sfUpdateDataTransform_script.png)
Below is the Javascript code:

```js
// You can reference the input variables using input.NAME
// Parsed JSON files could be referenced as fileInput
exports.step = function(input, fileInput) {
    const objectIDlist = [];
    const output = [];
    //add object_ids to a new list
    input.data.forEach((item) => \{
        const list = [];
        objectIDlist.push(item.object_id);
    \});
    //map input data's keys and values but skip the object_id 
    input.data.map((item, index) => {
        const keys = Object.keys(item);
        keys.splice(keys.indexOf("object_id"), 1);
        const fieldsArray = [];
        keys.forEach((key) => {
            const temp = {};
            if (key !== "object_id") \{
                temp.key = key;
                temp.value = item[key];
            \}
            fieldsArray.push(temp);
        })
        const returnObj = {};
        returnObj.fields = fieldsArray;
        //add object_id back to final object
        returnObj.object_id = objectIDlist[index];
        output.push(returnObj);
    })
    return output;
};
```

Explanation:

1. We prepare a list of object\_id values.
2. Map all keys except object\_id.
3. Add object\_id at the end to the final object.

### 2 - Preparing input for Salesforce create operation

We have the following dummy data:

### Dummy data

```json
[
  \{
    "LastName": "Lauren",
    "Company": "Web Design, Inc.",
    "Email": "laurenbailey123@ymail.com"
  \},
  \{
    "LastName": "Alexis",
    "Company": "Education Corp.",
    "Email": "awilson123@hotmail.com"
  \}
]
```

Our Salesforce Connector's create operation expects the following input for the above data:

### Required input schema

```json
[
   [
      \{
         "value":"Lauren",
         "key":"LastName"
      \},
      \{
         "value":"Web Design, Inc.",
         "key":"Company"
      \},
      \{
         "value":"laurenbailey@ymail.com",
         "key":"Email"
      \}
   ],
   [
      \{
         "value":"Alexis",
         "key":"LastName"
      \},
      \{
         "value":"Education Corp.",
         "key":"Company"
      \},
      \{
         "value":"awilson@hotmail.com",
         "key":"Email"
      \}
   ]
]
```

> **Info:** Please refer '[Determining Input schema](https://tray.io/documentation/tray-uac/building-automations/transforming-data-for-input/satisfying-input-schema/)' page which shows how to find out input and output data format for services.

You can import this project [here](#). It contains three ways you could solve this:

### 1 - Using core connectors

![sfCreateDataTransform nocode](https://tray.ai/documentation/images/platform/embedded/advanced-features/data-mapping/data-transformation/partner-defined-schema/5h8BoXikPUqArVyhOLbENL_sfCreateDataTransform_nocode.png)
Here we loop through the input array and use 'Data Storage' connector's 'Append to List' operation to add each subarray to the key 'batch\_create\_list'.

### 2 - Using JSON transformer

![sfCreateDataTransform jsonata](https://tray.ai/documentation/images/platform/embedded/advanced-features/data-mapping/data-transformation/partner-defined-schema/2rTE9AgAAk6sOjUEqWAWhb_sfCreateDataTransform_jsonata.png)
Below is the JSONata code:

```js
$map ($, function($item, $index){ 
    $map($keys($item), function($v, $i){
        \{
            'key' : $v,
            'value' : $lookup($item, $v)
        \}
    })
})
```

Explanation: For every object in the input array, we use another map operation to map its' keys and values.

### 3 - Using script

![sfCreateDataTransform script](https://tray.ai/documentation/images/platform/embedded/advanced-features/data-mapping/data-transformation/partner-defined-schema/3o4luuHsEafiToIn7IhafH_sfCreateDataTransform_script.png)
Below is the Javascript code:

```js
// You can reference the input variables using input.NAME
// Parsed JSON files could be referenced as fileInput
exports.step = function(input, fileInput) {
    const output = [];
    input.data.map((item, index) => {
        const keys = Object.keys(item);
        const fieldsArray = [];
        keys.forEach((key) => {
            const temp = {};
            temp.key = key;
            temp.value = item[key];
            fieldsArray.push(temp);
        })
        output.push(fieldsArray);
    })
    return output;
};
```

## Mailchimp

### 1 - Preparing input for Mailchimp batch subscribe operation

We have the following dummy data:

### Dummy data

```json
[
   \{
      "FNAME":"Anakin",
      "LNAME":"Skywalker",
      "email_address":"darthVader@empire.com",
      "status":"unsubscribed",
      "ADDRESS.addr1":"5, Tatooine Square",
      "ADDRESS.city":"Star city",
      "ADDRESS.state":"Star state",
      "ADDRESS.zip":"600007"
   \},
   \{
      "FNAME":"Luke",
      "LNAME":"Skywalker",
      "email_address":"luke@jedi.org",
      "status":"subscribed",
      "ADDRESS.addr1":"10, Rebel street",
      "ADDRESS.city":"Orange City",
      "ADDRESS.zip":"158732",
      "Address.country":"USA"
   \}
]
```

Our Mailchimp Connector's Raw HTTP operation to batch subscribe members expects the following input for the above data:

### Required input schema

```json
[
  {
    "email_address": "darthVader@empire.com",
    "status": "unsubscribed",
    "merge_fields": {
      "FNAME": "Anakin",
      "LNAME": "Skywalker",
      "ADDRESS": \{
        "addr1": "5, Tatooine Square",
        "city": "Star city",
        "state": "Star state",
        "zip": "600007"
      \}
    }
  },
  {
    "email_address": "luke@jedi.org",
    "status": "subscribed",
    "merge_fields": \{
      "FNAME": "Luke",
      "LNAME": "Skywalker"
    \}
  }
]
```

> **Info:** Please refer '[Determining Input schema](https://tray.io/documentation/tray-uac/building-automations/transforming-data-for-input/satisfying-input-schema/)' page which shows how to find out input and output data format for services.

You can import this project [here](#). It contains three ways you could solve this:

### 1 - Using JSON transformer

![data mapping4 JSONATA](https://tray.ai/documentation/images/platform/embedded/advanced-features/data-mapping/data-transformation/partner-defined-schema/1OJIeVNE3fcSzpfVfqgPd9_data_mapping4_JSONATA.png)
Below is the JSONata code:

```json
([
  $map($, function($item, $index) {
    (
      /* extract all keys from object*/
      $keysofObject := $keys($item);
      /* get merge_fields = all keys - 'email address' - 'status'*/
      $merge_fields_only := $filter($keysofObject, function($v, $i, $a) {$v != 'email_address' and $v != 'status'});
      /* form an array of non-address merge fields*/
      $otherFields := $filter($merge_fields_only, function($v, $i, $a) {$v ~> /^(?!.*ADDRESS).*/});
      /* form an array of address fields*/
      $addressFields := $filter($merge_fields_only, function($v, $i, $a) {$v ~> /^ADDRESS/});
      /* form an array of mandatory address fields*/
      $reqAddressFields := $filter($keysofObject, function($v, $i, $a) {
        $v = "ADDRESS.addr1" or $v = "ADDRESS.city" or $v = "ADDRESS.state" or $v = "ADDRESS.zip"
      });
      /* check if all 4 mandatory address fields are present, if not don't map ADDRESS*/
      $count($reqAddressFields) = 4 ?
      {
          'email_address': $item.email_address,
          'status': $item.status,
          'merge_fields': $merge([$map($otherFields, function($v, $i) {
              \{
                  $v: $lookup($item, $v)
              \}
          }),{
              "ADDRESS": $merge($map($addressFields, function($value, $index) {
              \{
                  $substringAfter($value, "ADDRESS."): $lookup($item, $value)
              \}
          }))
          }])
      } : /* else, here we don't map ADDRESS */
      {
          'email_address': $item.email_address,
          'status': $item.status,
          'merge_fields': $merge([$map($otherFields, function($v, $i) {
              \{
                  $v: $lookup($item, $v)
              \}
          })]) 
      }
    )
  })
])
```

### 2 - Using script

![dataMappingMC js](https://tray.ai/documentation/images/platform/embedded/advanced-features/data-mapping/data-transformation/partner-defined-schema/5h6ruKQRHkAJKfoJNHtZi8_dataMappingMC_js.png)
Below is the Javascript code:

```js
exports.step = function(input, fileInput) {
	//initialize output array
  let output = [];
  //map through each object
  input.data.map((item, index) => {
  	//get all keys of the object
    let allFields = Object.keys(item);
    //get only merge_fields
    let mergeFieldsOnly = allFields.filter((field)=> field !== 'email_address' && field !== 'status');
    //get non-address merge_fields
    let otherFields = mergeFieldsOnly.filter((field)=> field.match(/^(?!.*ADDRESS).*/));
    //get address fields
    let addressFields = mergeFieldsOnly.filter((field)=> field.match(/^ADDRESS/));
    //filter mandatory address fields
    let reqAddressFields = addressFields.filter((field)=> field === "ADDRESS.addr1" || field === "ADDRESS.city" || field === "ADDRESS.state" || field === "ADDRESS.zip")
    let outputObj = {};
    outputObj.email_address = item.email_address;
    outputObj.status = item.status;
    outputObj.merge_fields = new Object();
    //map non address fields
    for(let i=0 ; i<otherFields.length; i++)\{
      let fieldname = otherFields[i];
      outputObj.merge_fields[fieldname] = item[fieldname];
    \}
    //check if all mandatory address fields are present as address will only be mapped if they are
    if (reqAddressFields.length === 4){
      outputObj.merge_fields.ADDRESS = new Object();
      for(let i=0 ; i<addressFields.length; i++)\{
        let fieldname = addressFields[i].split('.')[1];
        outputObj.merge_fields.ADDRESS[fieldname] = item[addressFields[i]];
      \}
    }
    output.push(outputObj);
  });
return output;
};
```
