A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Collections.Generic;
6using System.Linq;
7
8namespace osu.Framework.Extensions.IEnumerableExtensions
9{
10 public static class EnumerableExtensions
11 {
12 /// <summary>
13 /// Performs an action on all the items in an IEnumerable collection.
14 /// </summary>
15 /// <typeparam name="T">The type of the items stored in the collection.</typeparam>
16 /// <param name="collection">The collection to iterate on.</param>
17 /// <param name="action">The action to be performed.</param>
18 public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
19 {
20 if (collection == null) return;
21
22 foreach (var item in collection)
23 action(item);
24 }
25
26 /// <summary>
27 /// Wraps this object instance into an <see cref="IEnumerable{T}"/>
28 /// consisting of a single item.
29 /// </summary>
30 /// <typeparam name="T">The type of the object.</typeparam>
31 /// <param name="item">The instance that will be wrapped.</param>
32 /// <returns> An <see cref="IEnumerable{T}"/> consisting of a single item.</returns>
33 public static IEnumerable<T> Yield<T>(this T item) => new[] { item };
34
35 /// <summary>
36 /// Retrieves the item after a pivot from an <see cref="IEnumerable{T}"/>.
37 /// </summary>
38 /// <typeparam name="T">The type of the items stored in the collection.</typeparam>
39 /// <param name="collection">The collection to iterate on.</param>
40 /// <param name="pivot">The pivot value.</param>
41 /// <returns>The item in <paramref name="collection"/> appearing after <paramref name="pivot"/>, or null if no such item exists.</returns>
42 public static T GetNext<T>(this IEnumerable<T> collection, T pivot)
43 {
44 return collection.SkipWhile(i => !EqualityComparer<T>.Default.Equals(i, pivot)).Skip(1).FirstOrDefault();
45 }
46
47 /// <summary>
48 /// Retrieves the item before a pivot from an <see cref="IEnumerable{T}"/>.
49 /// </summary>
50 /// <typeparam name="T">The type of the items stored in the collection.</typeparam>
51 /// <param name="collection">The collection to iterate on.</param>
52 /// <param name="pivot">The pivot value.</param>
53 /// <returns>The item in <paramref name="collection"/> appearing before <paramref name="pivot"/>, or null if no such item exists.</returns>
54 public static T GetPrevious<T>(this IEnumerable<T> collection, T pivot)
55 => collection.Reverse().GetNext(pivot);
56
57 /// <summary>
58 /// Returns the most common prefix of every string in this <see cref="IEnumerable{T}"/>
59 /// </summary>
60 /// <param name="collection">The string <see cref="IEnumerable{T}"/></param>
61 /// <returns>The most common prefix, or an empty string if no common prefix could be found.</returns>
62 /// <example>
63 /// "ab" == { "abc", "abd" }.GetCommonPrefix()
64 /// </example>
65 public static string GetCommonPrefix(this IEnumerable<string> collection)
66 {
67 ReadOnlySpan<char> prefix = default;
68
69 foreach (var str in collection)
70 {
71 if (prefix.IsEmpty) // the first string
72 {
73 prefix = str;
74 continue;
75 }
76
77 while (!prefix.IsEmpty)
78 {
79 if (str.AsSpan().StartsWith(prefix))
80 break;
81 else
82 prefix = prefix[..^1];
83 }
84
85 if (prefix.IsEmpty)
86 return string.Empty;
87 }
88
89 return new string(prefix);
90 }
91
92 /// <summary>
93 /// Get all combinations of provided sequences.
94 /// </summary>
95 public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
96 {
97 // https://stackoverflow.com/a/3098381
98 IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
99 return sequences.Aggregate(
100 emptyProduct,
101 (accumulator, sequence) =>
102 from accseq in accumulator
103 from item in sequence
104 select accseq.Concat(new[] { item })
105 );
106 }
107 }
108}