代码改变世界

扩展 IEnumerable<T>,让它根据另一个集合的顺序来排列

2014-04-03 16:47  音乐让我说  阅读(1008)  评论(0编辑  收藏  举报

假如我有两个集合:

public class Teacher
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class Student
{
    public int Id { get; set; }

    public string UserName { get; set; }

    public int TeacherId { get; set; }
}

集合代码:

static void Main(string[] args)
{
    IEnumerable<Teacher> teachers = new Teacher[]
    {
        new Teacher{ Id = 9, Name = "CCC" },
        new Teacher{ Id = 5, Name = "AAA" },
        new Teacher{ Id = 7, Name = "BBB" },
        new Teacher{ Id = 13, Name = "DDD" },
    };

    IEnumerable<Student> students = new Student[]
    {
        new Student{ Id = 1, TeacherId = 13, UserName = "张三" },
        new Student{ Id = 2, TeacherId = 5, UserName = "李四" },
        new Student{ Id = 3, TeacherId = 9, UserName = "王五" },
        new Student{ Id = 4, TeacherId = 18, UserName = "赵六" },
    };
}

前提条件:students 里面的 TeacherId 大部分来自于 teachers 里的 ID, 也可能不是。
现在要求:按 teachers 的 顺序 来对 students 里面的项排序。

我的代码如下:

首先扩展 IEnumerable<T>

public static class EnumerableExtensions
{
    /// <summary>
    /// 按照另一个现有的集合的关联字段来排序
    /// </summary>
    /// <typeparam name="T">类型1</typeparam>
    /// <typeparam name="T2">类型2</typeparam>
    /// <param name="source1">要排序的集合</param>
    /// <param name="source2">参考的集合</param>
    /// <param name="condition">条件</param>
    /// <returns></returns>
    public static IEnumerable<T> OrderByOther<T, T2>(this IEnumerable<T> source1, IEnumerable<T2> source2, Func<T, T2, bool> condition)
    {
        if (source1 == null)
        {
            throw new ArgumentNullException("source1");
        }
        if (source2 == null)
        {
            throw new ArgumentNullException("source2");
        }
        if (condition == null)
        {
            throw new ArgumentNullException("condition");
        }
        int source1Count = source1.Count();
        SortedDictionary<int, T> values = new SortedDictionary<int, T>();
        // 3, 0, 2, -1, -1
        for (int i = 0; i < source1Count; i++)
        {
            var item = source1.ElementAt(i);
            var tempIndex = source2.FirstIndex(s => condition(item, s));
            values.Add(tempIndex, item);
        }
        foreach (var item in values)
        {
            yield return item.Value;
        }
    }

    /// <summary>
    /// 得到满足条件的第一个元素在集合中所在的索引
    /// </summary>
    /// <typeparam name="T">类型</typeparam>
    /// <param name="source">目标集合</param>
    /// <param name="condition">条件</param>
    /// <returns>如果没有找到,返回 -1</returns>
    public static int FirstIndex<T>(this IEnumerable<T> source, Predicate<T> condition)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (condition == null)
        {
            throw new ArgumentNullException("condition");
        }
        int i = 0;
        foreach (var item in source)
        {
            if (condition(item))
            {
                return i;
            }
            i++;
        }
        return -1;
    }
}

然后测试代码如下:

static void Main(string[] args)
{
    IEnumerable<Teacher> teachers = new Teacher[]
    {
        new Teacher{ Id = 9, Name = "CCC" },
        new Teacher{ Id = 5, Name = "AAA" },
        new Teacher{ Id = 7, Name = "BBB" },
        new Teacher{ Id = 13, Name = "DDD" },
    };

    IEnumerable<Student> students = new Student[]
    {
        new Student{ Id = 1, TeacherId = 13, UserName = "张三" },
        new Student{ Id = 2, TeacherId = 5, UserName = "李四" },
        new Student{ Id = 3, TeacherId = 9, UserName = "王五" },
        new Student{ Id = 4, TeacherId = 18, UserName = "赵六" },
    };

    // 前提条件:students 里面的 TeacherId 大部分来自于 teachers 里的 ID, 也可能不是。
    // 现在要求:按 teachers 的 顺序 来对 students 里面的项排序。

    var result = students.OrderByOther(teachers, (a, b) => a.TeacherId == b.Id);
    foreach (var item in result)
    {
        Console.WriteLine("stuId:" + item.TeacherId + ", stuName:" + item.UserName);
    }
}

运行结果:

谢谢浏览!