What?
A quick article on how to use remove() on a map variable as well as a snippet of code to copy tags from a contact to a deal record.

Why?
Anything that stumps me for 30 minutes is worth writing a quick article on. In this case, I had a function querying tags on a contact record which needed to be copied to a deal record. I wanted to copy over the tag but apparently, the ID of the tag is not allowed to be copied over.

The official documentation says to use remove() to remove a key from a map. In this case, the ID key and value pair. It would then tell me the list was null when I thought I was adding the map with the ID removed back into a list. The map itself was null.

How?
We are using an API action called "Add_Tags". I'm going to get the tags of the contact record using an invokeurl because shortcode only gives me name and ID; but I want the color code field as well which I only seem to get with an InvokeURL. I'm then going to create a list map of tags and add these to the deal record.

The key take-away to remember here is that remove() is a void function, similar to the sort() function; in that if you try to assign this to a new variable name, it will fail.

In other words:
m_NewTag = m_Tag.remove("id");
// yields null

m_Tag.remove("id");
// returns m_Tag map without the ID key-value pair

The use-case
This is a snippet that gets the tags of a contact and copies it over to the deal record:
l_Tags = List();
//
r_DealDetails = zoho.crm.getRecordById("Deals",p_DealID);
if(!isNull(r_DealDetails.get("Contact_Name")))
{
	r_ContactDetails = invokeurl
	[
		url :"https://www.zohoapis.com/crm/v7/Contacts/" + r_DealDetails.get("Contact_Name").get("id")
		type :GET
		connection:"zcrm"
	];
	//
	l_ContactDetails = ifnull(r_ContactDetails.get("data"), List());
	for each m_ContactDetails in l_ContactDetails
    {
		if(!isNull(m_ContactDetails.get("Tag")))
		{
			for each m_Tag in m_ContactDetails.get("Tag")
			{
				m_Tag.remove("id");
				l_Tags.add(m_Tag);
			}
		}
		info l_Tags;
		//
		if(l_Tags.size()>0)
		{
			m_Params = {"tags": l_Tags};
			r_AddTags = invokeurl
			[
				url :"https://www.zohoapis.com/crm/v7/Deals/" + p_DealID + "/actions/add_tags"
				type :POST
				parameters:m_Params.toString()
				connection:"azcrm"
			];
			info r_AddTags;
		}		
    }
}

/*
{"code":"INVALID_DATA","details":{"api_name":"id","json_path":"$.tags[0].id"},"message":"invalid tag id","status":"error"}
-> Error was due to trying to assign map with removed key to a new map.
-> Solution was to remove the assignment and simply keep m_Tag.remove("id");

{"code":"MAPPING_MISMATCH","details":{"mapped_field":{"api_name":"name","json_path":"$.tags[0].name"},"api_name":"color_code","json_path":"$.tags[0].color_code"},"message":"Given tag present with different colour already.","status":"error"}
-> Error was due to someone already giving tags with the same name 
-> Solution is to remove the color_code key as well... m_Tag.remove("color_code");
*/

Considering that neither the color code nor ID were necessary in the end, I can use shortcode for getting the tag. Note that on a fresh CRM instance where no tags have been used previously, I would use the code above to maintain tag colors. Here's the shortened version where the system chooses the color for the tag:
l_Tags = List();
//
r_DealDetails = zoho.crm.getRecordById("Deals",p_DealID);
if(!isNull(r_DealDetails.get("Contact_Name")))
{
	m_ContactDetails = zoho.crm.getRecordById("Contacts", r_DealDetails.get("Contact_Name").get("id"));
	if(!isNull(m_ContactDetails.get("Tag")))
	{
		for each m_Tag in m_ContactDetails.get("Tag")
		{
			m_Tag.remove("id");
			l_Tags.add(m_Tag);
		}
	}
	info l_Tags;
	//
	if(l_Tags.size()>0)
	{
		m_Params = {"tags": l_Tags};
		r_AddTags = invokeurl
		[
			url :"https://www.zohoapis.com/crm/v7/Deals/" + p_DealID + "/actions/add_tags"
			type :POST
			parameters:m_Params.toString()
			connection:"zcrm"
		];
		info r_AddTags;
	}		
}

Fix to historical records
This snippet uses COQL to find me all the deals where the tag is empty but the contact record wasn't. There is an additional/repeat action to add tags; the first attempts to copy over the color on a new tag; the second is if this fails to copy over the color (because there is an existing tag with the same name), then it only copies over the name:
v_TotalCount = 0;
v_TotalProcessed = 0;
v_PerPage = 100;
v_CoqlQuery = "select id, Contact_Name.Tag 'Tag' from Deals where Tag is null and Contact_Name.Tag is not null order by id asc limit " + v_PerPage;
m_Params = {"select_query":v_CoqlQuery};
r_Coql = invokeurl
[
	url :"https://www.zohoapis.com/crm/v7/coql"
	type :POST
	parameters:m_Params.toString()
	connection:"zcrm"
];
if(!isNull(r_Coql.get("data")))
{
	for each  m_Data in r_Coql.get("data")
	{
		if(!isNull(m_Data.get("id")))
		{
			v_TotalCount = v_TotalCount + 1;
			//
			l_Tags = List();
			for each  m_Tag in m_Data.get("Tag")
			{
				m_Tag.remove("id");
				l_Tags.add(m_Tag);
			}
			if(l_Tags.size() > 0)
			{
				m_Params = {"tags":l_Tags};
				r_AddTags = invokeurl
				[
					url :"https://www.zohoapis.com/crm/v6/Deals/" + m_Data.get("id") + "/actions/add_tags"
					type :POST
					parameters:m_Params.toString()
					connection:"zcrm"
				];
				if(!isNull(r_AddTags.get("code")) && r_AddTags.get("code").equals("MAPPING_MISMATCH"))
				{
					info "Tags Error: MAPPING_MISMATCH: Color";
					info m_Tag.get("name") + " " + m_Tag.get("color_code");
					//
					l_Tags = List();
					for each  m_Tag in m_Data.get("Tag")
					{
						m_Tag.remove("id");
						m_Tag.remove("color_code");
						l_Tags.add(m_Tag);
					}
					m_Params = {"tags":l_Tags};
					r_AddTags = invokeurl
					[
						url :"https://www.zohoapis.com/crm/v7/Deals/" + m_Data.get("id") + "/actions/add_tags"
						type :POST
						parameters:m_Params.toString()
						connection:"zcrm"
					];
				}
				if(!isNull(r_AddTags.get("code")) && r_AddTags.get("code").equals("SUCCESS"))
				{
					info "Tag Error resolved.  Tags updated.";
					v_TotalProcessed = v_TotalProcessed + 1;
				}
			}
		}
		info "-------------------------";
	}
}
return "Updated " + v_TotalProcessed + " of " + v_TotalCount;