我正在寻找一种在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);
}
}
}