Q:

Merging 2 lists of different objects into a third list of a different type?

I'm building an API wrapper for a third party API that translates their objects into business domain objects that can be used for other processing. In this case, I need to take 2 different objects Contact and User and merge them into a single list of objects called UserContacts. I'm matching these objects based on their Email property, and if there is no matching elements, a new one is inserted.
Here are my current objects and methods, I'm just trying to figure out if there's a better/faster method.
public class ContactUser : IUser
{
    public string SalesForceUserId { get; set; }
    public string SalesForceContactId { get; set; }
    public string ZendeskId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

private List<IUser> MergeContactsAndUsers()
{
    var sfContacts = SalesForceCache.Contacts.Data;
    var sfUsers = SalesForceCache.Users.Data;

    var newUsers = sfUsers.Select(user => new ContactUser
    {
        SalesForceUserId = user.Id, 
        Name = user.Name, 
        FirstName = user.FirstName, 
        LastName = user.LastName, 
        Email = user.Email
    }).Cast<IUser>().ToList();

    foreach (var contact in sfContacts)
    {
        var tmp = newUsers.FirstOrDefault(n => n.Email == contact.Email);
        if (tmp != null)
        {
            tmp.SalesForceContactId = contact.Id;
        }
        else
        {
            var newUser = new ContactUser
            {
                SalesForceContactId = contact.Id,
                Name = contact.Name,
                FirstName = contact.FirstName,
                LastName = contact.LastName,
                Email = contact.Email
            };

            newUsers.Add(newUser);
        }
    }

    return newUsers;
}

A:

If you want to replace your current implementation with Join you can have something like this:
private List<IUser> MergeContactsAndUsers()
{
    var sfContacts = SalesForceCache.Contacts.Data;
    var sfUsers = SalesForceCache.Users.Data;

    var leftJoinResults =
            sfUsers.Join(
                sfContacts,
                u => u.Email,
                c => c.Email,
                (u, c) => new ContactUser()
                {
                    SalesForceContactId = c.SalesForceContactId,
                    SalesForceUserId = u.Id,
                    Name = u.Name,
                    FirstName = u.FirstName,
                    LastName = u.LastName,
                    Email = u.Email
                }).Cast<IUser>().ToList();

    var rightJoinResults =
        sfContacts
            .Where(c => !leftJoinResults.Select(nu => nu.SalesForceContactId).Contains(c.Id))
            .Select(c => new ContactUser
            {
                SalesForceContactId = c.Id,
                Name = c.Name,
                FirstName = c.FirstName,
                LastName = c.LastName,
                Email = c.Email
            });

    leftJoinResults.AddRange(rightJoinResults);
    return leftJoinResults;
}

But because Join is only a left join (and you need right join as well) it still requires an additional query to get missing contacts (the query to get rightJoinResults).
It's more of an alternative implementation with use of Join. Without proper measurements it's hard to tell whether it's faster.

