拆分列表,使重复项落在同一个子列表中

Splitting a list so duplicates fall within the same sub list

我有一个项目列表,我想将其分成大小均匀的子列表以进行异步处理。但是,需要同步处理具有相同值的项目,因此我需要一种有效的机制来确保将具有相同值的项目分组到同一子列表中,最好按照它们在源列表中出现的顺序。

所以对于这个例子...

public class ListItem
{
    public ListItem(string key)
    {
        this.Key = key;
    }

    public string Key;
}

private void Sample()
{
    List<ListItem> list = new List<ListItem>()
    {
        new ListItem("A"),
        new ListItem("B"),
        new ListItem("C"),
        new ListItem("D"),
        new ListItem("A"),
        new ListItem("B"),
        new ListItem("E"),
        new ListItem("F"),
        new ListItem("G"),
    };

    List<List<ListItem>> sublists = Partition(list, 3);
}

private List<List<ListItem>> Partition(List<ListItem> list, int batchSize)
{
    // Split list logic
}

Partition 方法会产生三个子列表,类似于

如果代码足够灵活以允许对多个列表项属性进行分组,这也会非常有用。

谢谢

试试这个:

public class ListItem
{
    public ListItem(string key) { Key=key; }
    public string Key { get; private set; }
    // ...
}

class Program
{
    static void Main(string[] args)
    {
        var list=new List<ListItem>() 
        {
            new ListItem("A"),
            new ListItem("B"),
            new ListItem("C"),
            new ListItem("D"),
            new ListItem("A"),
            new ListItem("B"),
            new ListItem("E"),
            new ListItem("F"),
            new ListItem("G"),
        };

        var sublists=Partition(list, 3);
        // 0: A, A, C
        // 1: B, B, D
        // 3: E, F, G
    }

    static List<List<ListItem>> Partition(IEnumerable<ListItem> list, int batchSize)
    {
        // create empty lists of batches
        var partitions=new List<List<ListItem>>();
        // group items with same key together
        var lookup=list.ToLookup((item) => item.Key);
        foreach(var group in lookup)
        {
            // Get array of same items (called a flock)
            ListItem[] flock=lookup[group.Key].ToArray();
            if(flock.Length>batchSize)
            {
                throw new ArgumentException("Batch Size is too small for grouping", "batchSize");
            }
            //Find first batch with available space from existing ones
            var batch=partitions.FirstOrDefault((part) => (batchSize-part.Count)>=flock.Length);
            if(batch!=null)
            {
                batch.AddRange(flock);
            }
            else // otherwise create a new batch
            {
                partitions.Add(new List<ListItem>(flock));
            }
        }
        return partitions;
    }
}