c#中如何对相似值数组进行分组


class Program
{
static void Main(string[] args)
{
int[] arr1 = { 1, 2, 3, 4 };
int[] arr2 = { 1, 2, 3 };
int[] arr3 = { 1, 2, 3 };
int[] arr4 = { 1, 2, 3, 4 };
int[] arr5 = { 1, 2, 3, 4 };
int[] arr6 = { 1, 2, 3, 4, 5 };
Order Order1 = new Order { OrderId = 1, Deatils = arr1 };
Order Order2 = new Order { OrderId = 2, Deatils = arr2 };
Order Order3 = new Order { OrderId = 3, Deatils = arr3 };
Order Order4 = new Order { OrderId = 4, Deatils = arr4 };
Order Order5 = new Order { OrderId = 5, Deatils = arr5 };
Order Order6 = new Order { OrderId = 6, Deatils = arr6 };
// I want to Output like this based on same values in details object.
string similarOrderDetailsOrderIds_1 = "1,4,5";
string similarOrderDetailsOrderIds_2 = "2,3";
string similarOrderDetailsOrderIds_3 = "5";
}
}
public class Order
{
public int OrderId { get; set; }
public int[] Deatils { get; set; }
}

我想这样输出,因为orderid -1,4,5在Details中有相同的值。

string similarOrderDetailsOrderIds_1 = "1,4,5";

string similarOrderDetailsOrderIds_2 = "2,3";

string similarOrderDetailsOrderIds_3 = "5";

此扩展方法是Michael Liu在此提供的解决方案的一般推广,该解决方案在生成哈希码之前对元素进行排序,并且适用于任何自身正确实现GetHashCode()Equals()的T:

static class Extensions
{
public static int GetCollectionHashCode<T>(this IEnumerable<T> e)
{
return ((IStructuralEquatable)e.OrderByDescending(x => x).ToArray())
.GetHashCode(EqualityComparer<T>.Default);
}
}

你可以这样使用它:

var orders = new[] { Order1, Order2, Order3, Order4, Order5, Order6 };
var groups = orders
.Where(x => x.Deatils != null)
.GroupBy(x => x.Deatils.GetCollectionHashCode())
.Select(x => x.Select(y => y.OrderId));

注意:上面的details打字错误是故意的。

您可以使用LINQ作为提供的其他答案,或者创建一个辅助方法来为您分组。

public static IEnumerable<IEnumerable<T>> Aggregate<T>(IEnumerable<T> ungrouped, Func<T, T, bool> Expression)
{
// create a place to put the result
List<List<T>> groupedItems = new();
// create a mutable storage to keep track which ones we shouldn't compare 
List<T> mutableBag = ungrouped.ToArray().ToList();
// n^2 isn't great but it was easy to implement, consider creating a hash from all elements and use those instead of manual comparisons
foreach (var left in ungrouped)
{
// create a place to store any similar items
List<T> similarItems = new();
// compare element to every remaining element, if they are similar add the other to the similar list
foreach (var right in mutableBag)
{
if (Expression(left, right))
{
similarItems.Add(right);
}
}
// if we didn't find any similar items continue and let GC collect similar items
if (similarItems.Count == 0)
{
continue;
}
// since we found items remove the from the mutable bag so we don't have duplicate entries
foreach (var item in similarItems)
{
mutableBag.Remove(item);
}
// add the similar items to the result
groupedItems.Add(similarItems);
}
return groupedItems;
}
public static bool Compare<T>(T[] Left, T[] Right)
{
// this compare method could be anything I would advise against doing member comparison like this, this is simplified to provide how to use the Aggregate method and what the CompareMethod might look like for comlpex types
if (Left.Length != Right.Length)
{
return false;
}
foreach (var item in Left)
{
if (Right.Contains(item) is false)
{
return false;
}
}
return true;
}

对于给定的示例

Order Order1 = new Order { OrderId = 1, Deatils = arr1 };
Order Order2 = new Order { OrderId = 2, Deatils = arr2 };
Order Order3 = new Order { OrderId = 3, Deatils = arr3 };
Order Order4 = new Order { OrderId = 4, Deatils = arr4 };
Order Order5 = new Order { OrderId = 5, Deatils = arr5 };
Order Order6 = new Order { OrderId = 6, Deatils = arr6 };
Order[] arrs = { Order1, Order2, Order3, Order4, Order5, Order6 };

我们可以这样使用这个新的辅助方法:

var groupedItems = Aggregate(arrs, (left, right) => Compare(left.Deatils, right.Deatils));

最新更新