如何正确转换集合?
How to transform collections correctly?
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
我有两个动漫类型的合集。假设他们调用了 a1 和 a2。我想获得 3 个动漫类型的合集:
- 仅在a1中的项目
- 仅在 a2 中的项目
- 两个集合中的项目
我怎样才能做到这一点?我想我应该用 linq 和 Id 值做点什么,但我没能找到合适的解决方案。
顺便说一句,每个动漫系列的Id值都是唯一的(如果两个项目具有相同的Id,则它们代表同一个系列)。
这是一种简单的方法,它具有在 LINQ over objects 和 LINQ over SQL:
中工作的额外好处
var q1=a1.Where(a=>!a2.Any(b=>b.Id==a.Id));
var q2=a2.Where(a=>!a1.Any(b=>b.Id==a.Id));
var a3=a1.Where(a=>a2.Any(b=>b.Id==a.Id));
来自 LINQPAD:
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
void Main()
{
var a1=new Anime[]{new Anime {Id=1,Title="Title1"},new Anime {Id=2,Title="Title2"}};
var a2=new Anime[]{new Anime {Id=2,Title="Title2"},new Anime {Id=3,Title="Title3"}};
var q1=a1.Where(a=>!a2.Any(b=>b.Id==a.Id));
var q2=a2.Where(a=>!a1.Any(b=>b.Id==a.Id));
var q3=a1.Where(a=>a2.Any(b=>b.Id==a.Id));
q1.Dump();
q2.Dump();
q3.Dump();
}
结果:
您也可以设置自定义 IEqualityComparer 并使用 Intersects,但这通常比在高性能情况下的所有情况都值得,而且在 SQL:[=17 上不适用于 LINQ =]
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
class AnimeComparer: IEqualityComparer<Anime>
{
public bool Equals(Anime a1, Anime a2)
{
return (a1.Id==a2.Id);
}
public int GetHashCode(Anime a)
{
return a.Id.GetHashCode();
}
}
void Main()
{
var a1=new Anime[]{new Anime {Id=1,Title="Title1"},new Anime {Id=2,Title="Title2"}};
var a2=new Anime[]{new Anime {Id=2,Title="Title2"},new Anime {Id=3,Title="Title3"}};
var ac=new AnimeComparer();
var q1=a1.Except(a2,ac);
var q2=a2.Except(a1,ac);
var q3=a1.Intersect(a2,ac);
q1.Dump();
q2.Dump();
q3.Dump();
}
第三种方法是使用扩展方法。这也不太可能在 SQL 上与 LINQ 一起使用,但不需要修改任何 类,或者如果您有多种对象类型则自定义 IEqualityComparers:
public static class LinqExtensions
{
public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> comparer)
{
return first.Where(x => !second.Any(y => comparer(x, y)));
}
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> comparer)
{
return first.Where(x => second.Any(y => comparer(x, y)));
}
}
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
void Main()
{
var a1=new Anime[]{new Anime {Id=1,Title="Title1"},new Anime {Id=2,Title="Title2"}};
var a2=new Anime[]{new Anime {Id=2,Title="Title2"},new Anime {Id=3,Title="Title3"}};
var q1=a1.Except(a2,(b1,b2)=>b1.Id==b2.Id);
var q2=a2.Except(a1,(b1,b2)=>b1.Id==b2.Id);
var q3=a1.Intersect(a2,(b1,b2)=>b1.Id==b2.Id);
q1.Dump();
q2.Dump();
q3.Dump();
}
第四个选项是使用 morelinq,它结合了无自定义 IEqualityComparers 的简单性和非常高的性能(但仍然不兼容 SQL 上的 LINQ):
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
void Main()
{
var a1=new Anime[]{new Anime {Id=1,Title="Title1"},new Anime {Id=2,Title="Title2"}};
var a2=new Anime[]{new Anime {Id=2,Title="Title2"},new Anime {Id=3,Title="Title3"}};
var q1=a1.ExceptBy(a2,k=>k.Id);
var q2=a2.ExceptBy(a1,k=>k.Id);
var q3=a1.ExceptBy(q1,k=>k.Id);
q1.Dump();
q2.Dump();
q3.Dump();
}
如果你实现了 GetHashCode 和 Equals 方法,你可以在 link 的帮助下轻松编写你的代码。
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
var anime = obj as Anime;
if (anime == null) return false;
return this.Id == anime.Id;
}
}
现在可以做到
var a1 = new List<Anime>()
{
new Anime() { Id=1, Title="Title1" },
new Anime() { Id=2, Title="Title2" },
new Anime() { Id=3, Title="Title3" },
new Anime() { Id=4, Title="Title4" }
};
var a2 = new List<Anime>()
{
new Anime() { Id=1, Title="Title1" },
new Anime() { Id=3, Title="Title3" },
new Anime() { Id=5, Title="Title5" }
};
var q1 = a1.Except(a2).ToList();
var q2 = a2.Except(a1).ToList();
var q3 = a1.Intersect(a2).ToList();
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
我有两个动漫类型的合集。假设他们调用了 a1 和 a2。我想获得 3 个动漫类型的合集:
- 仅在a1中的项目
- 仅在 a2 中的项目
- 两个集合中的项目
我怎样才能做到这一点?我想我应该用 linq 和 Id 值做点什么,但我没能找到合适的解决方案。 顺便说一句,每个动漫系列的Id值都是唯一的(如果两个项目具有相同的Id,则它们代表同一个系列)。
这是一种简单的方法,它具有在 LINQ over objects 和 LINQ over SQL:
中工作的额外好处var q1=a1.Where(a=>!a2.Any(b=>b.Id==a.Id));
var q2=a2.Where(a=>!a1.Any(b=>b.Id==a.Id));
var a3=a1.Where(a=>a2.Any(b=>b.Id==a.Id));
来自 LINQPAD:
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
void Main()
{
var a1=new Anime[]{new Anime {Id=1,Title="Title1"},new Anime {Id=2,Title="Title2"}};
var a2=new Anime[]{new Anime {Id=2,Title="Title2"},new Anime {Id=3,Title="Title3"}};
var q1=a1.Where(a=>!a2.Any(b=>b.Id==a.Id));
var q2=a2.Where(a=>!a1.Any(b=>b.Id==a.Id));
var q3=a1.Where(a=>a2.Any(b=>b.Id==a.Id));
q1.Dump();
q2.Dump();
q3.Dump();
}
结果:
您也可以设置自定义 IEqualityComparer 并使用 Intersects,但这通常比在高性能情况下的所有情况都值得,而且在 SQL:[=17 上不适用于 LINQ =]
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
class AnimeComparer: IEqualityComparer<Anime>
{
public bool Equals(Anime a1, Anime a2)
{
return (a1.Id==a2.Id);
}
public int GetHashCode(Anime a)
{
return a.Id.GetHashCode();
}
}
void Main()
{
var a1=new Anime[]{new Anime {Id=1,Title="Title1"},new Anime {Id=2,Title="Title2"}};
var a2=new Anime[]{new Anime {Id=2,Title="Title2"},new Anime {Id=3,Title="Title3"}};
var ac=new AnimeComparer();
var q1=a1.Except(a2,ac);
var q2=a2.Except(a1,ac);
var q3=a1.Intersect(a2,ac);
q1.Dump();
q2.Dump();
q3.Dump();
}
第三种方法是使用扩展方法。这也不太可能在 SQL 上与 LINQ 一起使用,但不需要修改任何 类,或者如果您有多种对象类型则自定义 IEqualityComparers:
public static class LinqExtensions
{
public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> comparer)
{
return first.Where(x => !second.Any(y => comparer(x, y)));
}
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> comparer)
{
return first.Where(x => second.Any(y => comparer(x, y)));
}
}
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
void Main()
{
var a1=new Anime[]{new Anime {Id=1,Title="Title1"},new Anime {Id=2,Title="Title2"}};
var a2=new Anime[]{new Anime {Id=2,Title="Title2"},new Anime {Id=3,Title="Title3"}};
var q1=a1.Except(a2,(b1,b2)=>b1.Id==b2.Id);
var q2=a2.Except(a1,(b1,b2)=>b1.Id==b2.Id);
var q3=a1.Intersect(a2,(b1,b2)=>b1.Id==b2.Id);
q1.Dump();
q2.Dump();
q3.Dump();
}
第四个选项是使用 morelinq,它结合了无自定义 IEqualityComparers 的简单性和非常高的性能(但仍然不兼容 SQL 上的 LINQ):
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
}
void Main()
{
var a1=new Anime[]{new Anime {Id=1,Title="Title1"},new Anime {Id=2,Title="Title2"}};
var a2=new Anime[]{new Anime {Id=2,Title="Title2"},new Anime {Id=3,Title="Title3"}};
var q1=a1.ExceptBy(a2,k=>k.Id);
var q2=a2.ExceptBy(a1,k=>k.Id);
var q3=a1.ExceptBy(q1,k=>k.Id);
q1.Dump();
q2.Dump();
q3.Dump();
}
如果你实现了 GetHashCode 和 Equals 方法,你可以在 link 的帮助下轻松编写你的代码。
class Anime
{
public int Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
var anime = obj as Anime;
if (anime == null) return false;
return this.Id == anime.Id;
}
}
现在可以做到
var a1 = new List<Anime>()
{
new Anime() { Id=1, Title="Title1" },
new Anime() { Id=2, Title="Title2" },
new Anime() { Id=3, Title="Title3" },
new Anime() { Id=4, Title="Title4" }
};
var a2 = new List<Anime>()
{
new Anime() { Id=1, Title="Title1" },
new Anime() { Id=3, Title="Title3" },
new Anime() { Id=5, Title="Title5" }
};
var q1 = a1.Except(a2).ToList();
var q2 = a2.Except(a1).ToList();
var q3 = a1.Intersect(a2).ToList();