将对象列表分隔为多个仅包含唯一项目的列表



我在C#应用程序中有一个需求,需要向api发送应用程序列表。api可以在一个调用中接收多个应用程序,但在api调用中每个人只能有一个应用程序。如果列表中有一个以上的应用程序,则必须将其删除,并在自己对api的调用中发送。

ID对于每个";人;数据中的内容无关紧要,bob可能有3个应用程序的数据设置为"0";A";并且它对于这个例子仍然是有效的。

这是的一个基本例子

我需要从这里开始,一个包含所有应用程序的单一列表:

{
[
{name: 'Bob', id: 1, data: 'A'},
{name: 'Jim', id: 2, data: 'A'},
{name: 'Sam', id: 3, data: 'A'},
{name: 'Bob', id: 1, data: 'B'},
{name: 'Bob', id: 1, data: 'C'},
{name: 'Sam', id: 3, data: 'B'},
{name: 'Bob', id: 4, data: 'Notice this is a different Bob'}
]
}

为此,多个列表中每个人只有一个应用程序,即bob在所有列表中,因为他有3个应用程序;sam在其中2个,jim只在其中1个

{
[
{name: 'Bob', id: 1, data: 'A'},
{name: 'Jim', id: 2, data: 'A'},
{name: 'Sam', id: 3, data: 'A'},
{name: 'Bob', id: 4, data: 'Notice this is a different Bob'}
],
[
{name: 'Bob', id: 1, data: 'B'},
{name: 'Sam', id: 3, data: 'B'}
],
[
{name: 'Bob', id: 1, data: 'C'}
]
}

我正试图找到一种有效的方法来做到这一点,而不需要创建嵌套循环。


更新编辑2

在LinqPad中使用默认的DemoDB,它附带了一个使用EmployeeID对测试数据使用订单表,以下内容似乎有效:

// make a copy of the orders table so as not to delete any actual data
List<Orders> lst = Orders.OrderBy(x => x.OrderID).ToList();
int x = 0; // only used as a counter for the console output
while (lst.Any()) {
Console.WriteLine($"List has {lst.Count()} orders");

List<Orders> lst1 = lst.OrderBy(x => x.OrderID).GroupBy(x => x.EmployeeID).Select(x => x.FirstOrDefault()).ToList();

Console.WriteLine($"List {x++} has {lst1.Count()} orders");

lst = lst.Where(x => !lst1.Contains(x)).ToList();

Console.Write(lst1);
}

有人能在这方面有所改进吗?

您基本上希望通过id对所有项目进行分组,然后转置分组。

因此(当仅考虑ids时(,列表[1, 2, 3, 1, 1, 3, 4]被转换为分组列表:

[
[1, 1, 1],
[2],
[3, 3],
[4]
]

然后进行转置(行变列,反之亦然(:

[
[1, 2, 3, 4],
[1,    3   ],
[1         ]
]

使用MoreLINQ软件包,您可以轻松做到这一点:

using System.Linq; // for GroupBy()
using MoreLinq;    // for Transpose()
// ...
var result = new[] {
new { name = "Bob", id = 1, data = "A" },
new { name = "Jim", id = 2, data = "A" },
new { name = "Sam", id = 3, data = "A" },
new { name = "Bob", id = 1, data = "B" },
new { name = "Bob", id = 1, data = "C" },
new { name = "Sam", id = 3, data = "B" },
new { name = "Bob", id = 4, data = "Notice this is a different Bob" }
}
.GroupBy(x => x.id)
.Transpose();

工作示例:

{id: 1, name: Bob, data: A}, {id: 2, name: Jim, data: A}, {id: 3, name: Sam, data: A}, {id: 4, name: Bob, data: Notice this is a different Bob}
{id: 1, name: Bob, data: B}, {id: 3, name: Sam, data: B}
{id: 1, name: Bob, data: C}

性能应该足够好。没有LINQ的优化实现总是更快,但大O复杂性无论如何都是一样的。在这种情况下,IMHO的清晰和简洁显然胜过了微观优化的实现。

类似于GoodNightNerdPride的解决方案和RandRandRandom的解决方案,但不使用外部依赖项,也不使用具有副作用的LINQ:

UserApplication[] source = new[]
{
new UserApplication() { Id = 1, Name = "Bob", Data = "A"},
new UserApplication() { Id = 2, Name = "Jim", Data = "A"},
new UserApplication() { Id = 3, Name = "Sam", Data = "A"},
new UserApplication() { Id = 1, Name = "Bob", Data = "B"},
new UserApplication() { Id = 1, Name = "Bob", Data = "C"},
new UserApplication() { Id = 3, Name = "Sam", Data = "B"},
new UserApplication() { Id = 4, Name = "Bob", Data = "A"} // Different Bob
};
List<UserApplication>[] lists = source
.GroupBy(x => x.Id)
.SelectMany(g => g.Select((x, i) => (Item: x, Ordinal: i)))
.GroupBy(entry => entry.Ordinal)
.Select(g => g.Select(entry => entry.Item).ToList())
.ToArray();
foreach (var list in lists)
{
Console.WriteLine(String.Join(", ",
list.Select(a => $"(#{a.Id} {a.Name}: {a.Data})")));
}

第一个GroupBy通过Id对应用程序进行分组,然后将Ordinal分配给分组在一起的应用程序。然后组被展平,并且第二GroupBy通过Ordinal再次对应用进行分组。最后丢弃CCD_ 9。

输出:

(#1 Bob: A), (#2 Jim: A), (#3 Sam: A), (#4 Bob: A)
(#1 Bob: B), (#3 Sam: B)
(#1 Bob: C)

示例中使用的UserApplication类:

class UserApplication
{
public int Id { get; set; }
public string Name { get; set; }
public string Data { get; set; }
}

试试这个

var array = new List<Example>
{
new Example{ Name = "Bob", Id = 1, Data = "A" },
new Example{ Name = "Jim", Id = 2, Data = "A" },
new Example{ Name = "Sam", Id = 3, Data = "A" },
new Example{ Name = "Bob", Id = 1, Data = "B" },
new Example{ Name = "Bob", Id = 1, Data = "C" },
new Example{ Name = "Sam", Id = 3, Data = "B" },
new Example{ Name = "Bob", Id = 4, Data = "Notice this is a different Bob" },
};
var dict = new Dictionary<int, int>();
var x = array.GroupBy(x =>
{
dict.TryGetValue(x.Id, out var value);
dict[x.Id] = ++value;
return value;
}).ToList();

如果我没有记错的话,这基本上是阿玛丹的主意。

将名称的Dictionary转换为整数。对于每个应用程序,查看到目前为止您看到的那个人有多少应用程序(如果还没有,则为0(;将该应用程序插入结果的那一行(如果它还不存在,则创建该行((例如,如果Bob的计数为0,则插入第0行(,然后递增计数(例如,我们现在看到1个Bob;下一个Bob将进入第一行(。复杂性为O(N(。

您可以使用LINQ中的GroupBy()方法按需要的属性对集合进行分组。尝试以下代码:

public class Example
{
public string Name { get; set; }
public long Id { get; set; }
public string Data { get; set; }
}
var array = new[]
{
new Example{ Name = "Bob", Id = 1, Data = "A" },
new Example{ Name = "Jim", Id = 2, Data = "A" },
new Example{ Name = "Sam", Id = 3, Data = "A" },
new Example{ Name = "Bob", Id = 1, Data = "B" },
new Example{ Name = "Bob", Id = 1, Data = "C" },
new Example{ Name = "Sam", Id = 3, Data = "B" },
};
var groups = array.GroupBy(x => x.Data);

UPDATE为了获得请求的结果,我以以下代码结束:

var array = new List<Example>
{
new Example{ Name = "Bob", Id = 1, Data = "A" },
new Example{ Name = "Jim", Id = 2, Data = "A" },
new Example{ Name = "Sam", Id = 3, Data = "A" },
new Example{ Name = "Bob", Id = 1, Data = "B" },
new Example{ Name = "Bob", Id = 1, Data = "C" },
new Example{ Name = "Sam", Id = 3, Data = "B" },
new Example{ Name = "Bob", Id = 4, Data = "Notice this is a different Bob" },
};
// group entries by name so we know we will have [nameCount,cols] dimensional result
var groups = array.GroupBy(x => x.Name).ToDictionary(x => x.Key, x => x.OrderBy(x => x.Id).ToArray());
// Initialize result as multidimensional list
var output = groups.Select(x => new List<Example>()).ToList();
foreach (var group in groups)
{
int j = 0; // <= index of item in output
for (int i = 0; i < group.Value.Length; i++)
{
// check if output[j] doesn't contain item with same name and id
if (output[j].FirstOrDefault(x => x.Name == group.Value[i].Name && x.Id == group.Value[i].Id) == null)
{
output[j].Add(group.Value[i]);
j++;
}
else
{
j++;
i--;
}

if (j == output.Count) j = 0; // <= reset counter
}
}

最新更新