交错排列一组电子邮件地址,以避免具有相同域的项目连续



我正在寻找一种在C#中对电子邮件地址数组进行排序的有效方法,以避免具有相同域的项目是连续的。

数组中的电子邮件地址已经不同,并且都是小写。

示例:

给定一个包含以下条目的数组:

john.doe@domain1.com
jane_doe@domain1.com
patricksmith@domain2.com
erick.brown@domain3.com

我想获得类似于以下内容的东西:

john.doe@domain1.com    
patricksmith@domain2.com
jane_doe@domain1.com
erick.brown@domain3.com

借助扩展方法(从https://stackoverflow.com/a/27533369/172769),你可以这样做:

List<string> emails = new List<string>();
emails.Add("john.doe@domain1.com");
emails.Add("jane_doe@domain1.com");
emails.Add("patricksmith@domain2.com");
emails.Add("erick.brown@domain3.com");
var q = emails.GroupBy(m => m.Split('@')[1]).Select(g => new List<string>(g)).Interleave();

Interleave方法定义为:

public static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source )
{
    var queues = source.Select(x => new Queue<T>(x)).ToList();
    while (queues.Any(x => x.Any())) {
        foreach (var queue in queues.Where(x => x.Any())) {
            yield return queue.Dequeue();
        }
    }
}

因此,基本上,我们根据电子邮件地址的域部分创建组,将每个组投影(或选择)到List<string>中,然后"交错"这些列表。

我已经根据您的样本数据进行了测试,但可能需要更彻底的测试来找到边缘案例。

DotNetFiddle片段

干杯

这将使它们半均匀地分布,并试图避免相邻的域匹配(尽管在某些列表中这可能是不可能的)。这个答案将使用OOP和Linq。

DotNetFiddle.Net示例

using System;
using System.Linq;
using System.Collections.Generic;
                    
public class Program
{
    public static void Main()
    {
        var seed = new List<string>()
        {
            "1@a.com",
            "2@a.com",
            "3@a.com",
            "4@a.com",
            "5@a.com",
            "6@a.com",
            "7@a.com",
            "8@a.com",
            "9@a.com",
            "10@a.com",
            
            "1@b.com",
            "2@b.com",
            "3@b.com",
            "1@c.com",
            "4@b.com",
            "2@c.com",
            "3@c.com",
            "4@c.com"
        };
        
        var work = seed
            // Create a list of EmailAddress objects
            .Select(s => new EmailAddress(s)) // s.ToLowerCase() ?
            // Group the list by Domain
            .GroupBy(s => s.Domain)
            // Create a List<EmailAddressGroup>
            .Select(g => new EmailAddressGroup(g))
            .ToList();
        
        var currentDomain = string.Empty;
        while(work.Count > 0)
        {
            // this list should not be the same domain we just used
            var noDups = work.Where(w => w.Domain != currentDomain);
            // if none exist we are done, or it can't be solved
            if (noDups.Count() == 0)
            {
                break;
            }
            // find the first group with the most items
            var workGroup = noDups.First(w => w.Count() == noDups.Max(g => g.Count()));
            // get the email address and remove it from the group list
            var workItem = workGroup.Remove();
            
            // if the group is empty remove it from *work*
            if (workGroup.Count() == 0)
            {
                work.Remove(workGroup);
                Console.WriteLine("removed: " + workGroup.Domain);
            }
            
            Console.WriteLine(workItem.FullEmail);
            
            // last domain looked at.
            currentDomain = workItem.Domain;
        }
        
        Console.WriteLine("Cannot disperse email addresses affectively, left overs:");
        
        foreach(var workGroup in work)
        {
            while(workGroup.Count() > 0)
            {
                var item = workGroup.Remove();
                Console.WriteLine(item.FullEmail);
            }
        }
            
            
    }
    
    public class EmailAddress
    {
        public EmailAddress(string emailAddress)
        {
            // Additional Email Address Validation
            
            var result = emailAddress.Split(new char[] {'@'}, StringSplitOptions.RemoveEmptyEntries)
                .ToList();
            
            if (result.Count() != 2)
            {
                new ArgumentException("emailAddress");
            }
            
            this.FullEmail = emailAddress;
            this.Name = result[0];
            this.Domain = result[1];
        }
        
        public string Name { get; private set; }
        public string Domain { get; private set; }
        public string FullEmail { get; private set; }
    }
    
    public class EmailAddressGroup
    {
        private List<EmailAddress> _emails;
        
        public EmailAddressGroup(IEnumerable<EmailAddress> emails)
        {
            this._emails = emails.ToList();
            this.Domain = emails.First().Domain;
        }
        
        public int Count()
        {
            return _emails.Count();
        }
        
        public string Domain { get; private set; }
        
        public EmailAddress Remove()
        {
            var result = _emails.First();
            _emails.Remove(result);
            return result;
        }
    }
}

输出:

1@a.com

1@b.com

2@a.com

1@c.com

3@a.com

2@b.com

4@a.com

2@c.com

5@a.com

3@b.com

6@a.com

3@c.com

7@a.com

已删除:b.com

4@b.com

8@a.com

已删除:c.com

4@c.com

9@a.com

无法深情地分散电子邮件地址,遗留问题:

10@a.com

这样的东西会平均分布它们,但在新列表的末尾会出现问题(=连续元素)。。。

        var list = new List<string>();
        list.Add("john.doe@domain1.com");
        list.Add("jane_doe@domain1.com");
        list.Add("patricksmith@domain2.com");
        list.Add("erick.brown@domain3.com");
        var x = list.GroupBy(content => content.Split('@')[1]);
        var newlist = new List<string>();
        bool addedSomething=true;
        int i = 0;
        while (addedSomething) {
            addedSomething = false;
            foreach (var grp in x) {
                if (grp.Count() > i) {
                    newlist.Add(grp.ElementAt(i));
                    addedSomething = true;
                }
            }
            i++;
        }

编辑:添加了高级描述:)

该代码所做的是按域对每个元素进行分组,按大小降序对组进行排序(首先是最大的组),将每个组的元素投影到一个堆栈中,然后从每个堆栈中弹出(总是用不同的域从最大的堆栈中弹出下一个元素)。如果只剩下一个堆栈,那么就会产生它的内容。

这应该确保所有域尽可能均匀地分布。

MaxBy扩展方法来自:https://stackoverflow.com/a/31560586/969962

private IEnumerable<string> GetNonConsecutiveEmails(List<string> list)
{
    var emailAddresses = list.Distinct().Select(email => new EmailAddress { Email = email, Domain = email.Split('@')[1]}).ToArray();
    var groups = emailAddresses
                .GroupBy(addr => addr.Domain)
                .Select (group => new { Domain = group.Key, EmailAddresses = new Stack<EmailAddress>(group)})
                .ToList();
    EmailAddress lastEmail = null;
    while(groups.Any(g => g.EmailAddresses.Any()))
    {
        // Try and pick from the largest stack.
        var stack = groups
            .Where(g => (g.EmailAddresses.Any()) && (lastEmail == null ? true : lastEmail.Domain != g.Domain))
            .MaxBy(g => g.EmailAddresses.Count);
        // Null check to account for only 1 stack being left.
        // If so, pop the elements off the remaining stack.   
        lastEmail = (stack ?? groups.First(g => g.EmailAddresses.Any())).EmailAddresses.Pop();
        yield return lastEmail.Email;
    }
}
class EmailAddress
{
    public string Domain;
    public string Email;
}
public static class Extensions
{
    public static T MaxBy<T,U>(this IEnumerable<T> data, Func<T,U> f) where U:IComparable
    {
        return data.Aggregate((i1, i2) => f(i1).CompareTo(f(i2))>0 ? i1 : i2);
    }
}

我在这里要做的是首先对它们进行排序。然后我从另一端重新安排。我相信有更有效的方法可以做到这一点,但这是一个简单的方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            String[] emails = { "john.doe@domain1.com", "jane_doe@domain1.com", "patricksmith@domain2.com", "erick.brown@domain3.com" };
            var result = process(emails);
        }
        static String[] process(String[] emails)
        {
            String[] result = new String[emails.Length];
            var comparer = new DomainComparer();
            Array.Sort(emails, comparer);
            for (int i = 0, j = emails.Length - 1, k = 0; i < j; i++, j--, k += 2)
            {
                if (i == j)
                    result[k] = emails[i];
                else
                {
                    result[k] = emails[i];
                    result[k + 1] = emails[j];
                }
            }
            return result;
        }
    }
    public class DomainComparer : IComparer<string>
    {
        public int Compare(string left, string right)
        {
            int at_pos = left.IndexOf('@');
            var left_domain = left.Substring(at_pos, left.Length - at_pos);
            at_pos = right.IndexOf('@');
            var right_domain = right.Substring(at_pos, right.Length - at_pos);
            return String.Compare(left_domain, right_domain);
        }
    }
}

最新更新