private static IEnumerable <IEnumerable <T> > GenerateCombinations <T>(this IEnumerable <T> source, int combinationSize, CombinationsGenerationMode mode) { if (source == null) { throw new ArgumentNullException(nameof(source), "Source collection is null"); } if (combinationSize == 0) { return(new[] { new T[] { } }); } IEnumerable <T> sourceList = source as IList <T> ?? source.ToList(); CombinationsGenerationMode[] distinctModes = new[] { CombinationsGenerationMode.DistinctOrderSensitive, CombinationsGenerationMode.DistinctOrderInsensitive }; CombinationsGenerationMode[] orderSensitiveModes = new[] { CombinationsGenerationMode.AllowDuplicatesOrderSensitive, CombinationsGenerationMode.DistinctOrderSensitive }; if (distinctModes.Contains(mode) && (combinationSize > sourceList.Count())) { return(new List <IEnumerable <T> >()); } if (sourceList.Count() == 1) { return(new[] { sourceList }); } var indexedSource = sourceList .Select((x, i) => new { Item = x, Index = i }) .ToList(); return(indexedSource .SelectMany(x => indexedSource .OrderBy(y => x.Index != y.Index) .Skip(distinctModes.Contains(mode) ? 1 : 0) .OrderBy(y => y.Index) .Skip(orderSensitiveModes.Contains(mode) ? 0 : x.Index) .GenerateCombinations(combinationSize - 1, mode) .Select(y => new[] { x }.Concat(y).Select(z => z.Item)) )); }
/// <summary> /// Generates all possible subsets of a given size that can be taken from a set. Distinct orderings are counted as distinct sets. /// </summary> /// <param name="source">A set of items</param> /// <param name="combinationSize">The number of items per result set</param> /// <param name="mode">The desired mode: whether to use each item from the source set only once, or allow re-use. (Note that Distinct mode requires a <paramref name="combinationSize"/> equal to or greater than the source collection length.)</param> /// <returns></returns> public static IEnumerable <IEnumerable <T> > Combinations <T>(this IEnumerable <T> source, int combinationSize, CombinationsGenerationMode mode = CombinationsGenerationMode.DistinctOrderInsensitive) { if (combinationSize < 1) { throw new ArgumentOutOfRangeException(nameof(combinationSize), "Combination size must be positive and non-zero"); } return(source.GenerateCombinations(combinationSize, mode)); }