Skip to content

Commit b1c69ba

Browse files
authored
Merge pull request #52 from sharpcode-it/develop
Develop into Master
2 parents 7ed0543 + 39e3df5 commit b1c69ba

9 files changed

+549
-103
lines changed

SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
11-
<PackageReference Include="MSTest.TestAdapter" Version="3.5.2" />
12-
<PackageReference Include="MSTest.TestFramework" Version="3.5.2" />
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
11+
<PackageReference Include="MSTest.TestAdapter" Version="3.6.3" />
12+
<PackageReference Include="MSTest.TestFramework" Version="3.6.3" />
1313
</ItemGroup>
1414

1515
<ItemGroup>

SharpHelpers/SharpHelpers/BooleanHelper.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// (c) 2019 SharpCoding
22
// This code is licensed under MIT license (see LICENSE.txt for details)
3+
using System;
4+
35
namespace SharpCoding.SharpHelpers
46
{
57
public static class BooleanHelper
@@ -49,5 +51,51 @@ public static string ToStringValues(this bool? value, string trueValue, string f
4951
{
5052
return value.HasValue ? (value.Value ? trueValue : falseValue) : falseValue;
5153
}
54+
55+
/// <summary>
56+
/// Negates the instance boolean value.
57+
/// </summary>
58+
/// <param name="instance">The boolean value to negate.</param>
59+
/// <returns>Returns the negated boolean value.</returns>
60+
public static bool Not(this bool instance)
61+
{
62+
return !instance;
63+
}
64+
65+
/// <summary>
66+
/// Determines if the instance is true and executes the specified action if true.
67+
/// </summary>
68+
/// <param name="instance">The boolean value to evaluate.</param>
69+
/// <param name="action">The action to execute if the boolean value is true.</param>
70+
public static void IfTrue(this bool instance, Action action)
71+
{
72+
if (instance)
73+
{
74+
action?.Invoke();
75+
}
76+
}
77+
78+
/// <summary>
79+
/// Determines if the instance is false and executes the specified action if false.
80+
/// </summary>
81+
/// <param name="instance">The boolean value to evaluate.</param>
82+
/// <param name="action">The action to execute if the boolean value is false.</param>
83+
public static void IfFalse(this bool instance, Action action)
84+
{
85+
if (!instance)
86+
{
87+
action?.Invoke();
88+
}
89+
}
90+
91+
/// <summary>
92+
/// Returns the boolean value as an integer (1 for true, 0 for false).
93+
/// </summary>
94+
/// <param name="instance">The boolean value to convert.</param>
95+
/// <returns>1 if true, 0 if false.</returns>
96+
public static int ToInt(this bool instance)
97+
{
98+
return instance ? 1 : 0;
99+
}
52100
}
53101
}

SharpHelpers/SharpHelpers/DataTableHelper.cs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Data;
6+
using System.Linq;
67
using System.Reflection;
78

89
namespace SharpCoding.SharpHelpers
@@ -51,6 +52,139 @@ public static DataTable SetColumnsOrder(this DataTable table, string[] columnNam
5152
list.Add(objClass);
5253
}
5354
return list;
55+
}
56+
57+
/// <summary>
58+
/// Converts the DataTable to a CSV format string.
59+
/// </summary>
60+
/// <param name="table"></param>
61+
/// <param name="delimiter"></param>
62+
/// <returns></returns>
63+
public static string ToCsv(this DataTable table, string delimiter = ",")
64+
{
65+
if (table == null) throw new ArgumentNullException(nameof(table));
66+
67+
var csv = new List<string>();
68+
var headers = string.Join(delimiter, table.Columns.Cast<DataColumn>().Select(c => c.ColumnName));
69+
csv.Add(headers);
70+
71+
foreach (DataRow row in table.Rows)
72+
{
73+
var line = string.Join(delimiter, row.ItemArray.Select(field => field?.ToString()));
74+
csv.Add(line);
75+
}
76+
return string.Join(Environment.NewLine, csv);
77+
}
78+
79+
/// <summary>
80+
/// Adds a new column to the DataTable with the specified default value.
81+
/// </summary>
82+
/// <param name="table"></param>
83+
/// <param name="columnName"></param>
84+
/// <param name="defaultValue"></param>
85+
/// <typeparam name="T"></typeparam>
86+
public static void AddColumn<T>(this DataTable table, string columnName, T defaultValue = default)
87+
{
88+
if (table == null) throw new ArgumentNullException(nameof(table));
89+
90+
var column = new DataColumn(columnName, typeof(T)) { DefaultValue = defaultValue };
91+
table.Columns.Add(column);
92+
foreach (DataRow row in table.Rows)
93+
{
94+
row[columnName] = defaultValue;
95+
}
96+
}
97+
98+
/// <summary>
99+
/// Merges multiple DataTables with the same schema into one.
100+
/// </summary>
101+
/// <param name="tables"></param>
102+
/// <returns></returns>
103+
/// <exception cref="ArgumentException"></exception>
104+
public static DataTable MergeTables(IEnumerable<DataTable> tables)
105+
{
106+
if (tables == null) throw new ArgumentNullException(nameof(tables));
107+
108+
var resultTable = tables.First().Clone();
109+
foreach (var table in tables)
110+
{
111+
if (!AreSchemasCompatible(resultTable, table))
112+
throw new ArgumentException("Tables have incompatible schemas.");
113+
114+
foreach (DataRow row in table.Rows)
115+
{
116+
resultTable.ImportRow(row);
117+
}
118+
}
119+
return resultTable;
120+
}
121+
122+
private static bool AreSchemasCompatible(DataTable table1, DataTable table2)
123+
{
124+
if (table1.Columns.Count != table2.Columns.Count) return false;
125+
126+
for (int i = 0; i < table1.Columns.Count; i++)
127+
{
128+
if (table1.Columns[i].ColumnName != table2.Columns[i].ColumnName ||
129+
table1.Columns[i].DataType != table2.Columns[i].DataType)
130+
return false;
131+
}
132+
return true;
133+
}
134+
135+
/// <summary>
136+
/// Filters the rows in the DataTable based on a predicate.
137+
/// </summary>
138+
/// <param name="table"></param>
139+
/// <param name="predicate"></param>
140+
/// <returns></returns>
141+
public static DataTable Filter(this DataTable table, Func<DataRow, bool> predicate)
142+
{
143+
if (table == null) throw new ArgumentNullException(nameof(table));
144+
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
145+
146+
var filteredTable = table.Clone();
147+
foreach (DataRow row in table.AsEnumerable().Where(predicate))
148+
{
149+
filteredTable.ImportRow(row);
150+
}
151+
return filteredTable;
152+
}
153+
154+
/// <summary>
155+
/// Checks if the DataTable is empty (contains no rows).
156+
/// </summary>
157+
/// <param name="table"></param>
158+
/// <returns></returns>
159+
public static bool IsEmpty(this DataTable table)
160+
{
161+
if (table == null) throw new ArgumentNullException(nameof(table));
162+
163+
return table.Rows.Count == 0;
164+
}
165+
166+
/// <summary>
167+
/// Removes duplicate rows based on specified columns.
168+
/// </summary>
169+
/// <param name="table"></param>
170+
/// <param name="columnNames"></param>
171+
/// <returns></returns>
172+
public static DataTable RemoveDuplicates(this DataTable table, params string[] columnNames)
173+
{
174+
if (table == null) throw new ArgumentNullException(nameof(table));
175+
176+
var distinctTable = table.Clone();
177+
var uniqueRows = new HashSet<string>();
178+
179+
foreach (DataRow row in table.Rows)
180+
{
181+
var key = string.Join("|", columnNames.Select(c => row[c]?.ToString() ?? ""));
182+
if (uniqueRows.Add(key))
183+
{
184+
distinctTable.ImportRow(row);
185+
}
186+
}
187+
return distinctTable;
54188
}
55189
}
56190
}

SharpHelpers/SharpHelpers/EnumerableHelper.cs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,5 +268,122 @@ public static int Sum<T>(this IEnumerable<T> source, Func<T, int> selector)
268268

269269
return source.Select(selector).Sum();
270270
}
271+
272+
/// <summary>
273+
/// Returns the maximum element based on a given selector function.
274+
/// </summary>
275+
/// <typeparam name="TSource"></typeparam>
276+
/// <typeparam name="TKey"></typeparam>
277+
/// <param name="source"></param>
278+
/// <param name="selector"></param>
279+
/// <returns></returns>
280+
/// <exception cref="ArgumentNullException"></exception>
281+
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
282+
where TKey : IComparable<TKey>
283+
{
284+
if (source == null) throw new ArgumentNullException(nameof(source));
285+
if (selector == null) throw new ArgumentNullException(nameof(selector));
286+
287+
return source.Aggregate((maxItem, nextItem) => selector(nextItem).CompareTo(selector(maxItem)) > 0 ? nextItem : maxItem);
288+
}
289+
290+
/// <summary>
291+
/// Returns the minimum element based on a given selector function.
292+
/// </summary>
293+
/// <typeparam name="TSource"></typeparam>
294+
/// <typeparam name="TKey"></typeparam>
295+
/// <param name="source"></param>
296+
/// <param name="selector"></param>
297+
/// <returns></returns>
298+
/// <exception cref="ArgumentNullException"></exception>
299+
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
300+
where TKey : IComparable<TKey>
301+
{
302+
if (source == null) throw new ArgumentNullException(nameof(source));
303+
if (selector == null) throw new ArgumentNullException(nameof(selector));
304+
305+
return source.Aggregate((minItem, nextItem) => selector(nextItem).CompareTo(selector(minItem)) < 0 ? nextItem : minItem);
306+
}
307+
308+
/// <summary>
309+
/// Finds the index of the first element that satisfies a given predicate.
310+
/// </summary>
311+
/// <typeparam name="T"></typeparam>
312+
/// <param name="source"></param>
313+
/// <param name="predicate"></param>
314+
/// <returns></returns>
315+
/// <exception cref="ArgumentNullException"></exception>
316+
public static int FindIndex<T>(this IEnumerable<T> source, Func<T, bool> predicate)
317+
{
318+
if (source == null) throw new ArgumentNullException(nameof(source));
319+
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
320+
321+
int index = 0;
322+
foreach (var item in source)
323+
{
324+
if (predicate(item)) return index;
325+
index++;
326+
}
327+
return -1;
328+
}
329+
330+
/// <summary>
331+
/// Checks if the source contains any of the specified items.
332+
/// </summary>
333+
/// <typeparam name="T"></typeparam>
334+
/// <param name="source"></param>
335+
/// <param name="items"></param>
336+
/// <returns></returns>
337+
/// <exception cref="ArgumentNullException"></exception>
338+
public static bool ContainsAny<T>(this IEnumerable<T> source, params T[] items)
339+
{
340+
if (source == null) throw new ArgumentNullException(nameof(source));
341+
if (items == null) throw new ArgumentNullException(nameof(items));
342+
343+
var set = new HashSet<T>(items);
344+
return source.Any(set.Contains);
345+
}
346+
347+
/// <summary>
348+
/// Checks if the source contains all of the specified items.
349+
/// </summary>
350+
/// <typeparam name="T"></typeparam>
351+
/// <param name="source"></param>
352+
/// <param name="items"></param>
353+
/// <returns></returns>
354+
/// <exception cref="ArgumentNullException"></exception>
355+
public static bool ContainsAll<T>(this IEnumerable<T> source, params T[] items)
356+
{
357+
if (source == null) throw new ArgumentNullException(nameof(source));
358+
if (items == null) throw new ArgumentNullException(nameof(items));
359+
360+
var set = new HashSet<T>(source);
361+
return items.All(set.Contains);
362+
}
363+
364+
/// <summary>
365+
/// Returns the median of a sequence of numbers.
366+
/// </summary>
367+
/// <param name="source"></param>
368+
/// <returns></returns>
369+
/// <exception cref="ArgumentNullException"></exception>
370+
public static double Median(this IEnumerable<int> source)
371+
{
372+
if (source == null) throw new ArgumentNullException(nameof(source));
373+
374+
var sortedList = source.OrderBy(n => n).ToList();
375+
int count = sortedList.Count;
376+
if (count == 0)
377+
throw new InvalidOperationException("The source sequence is empty.");
378+
379+
if (count % 2 == 0)
380+
{
381+
return (sortedList[count / 2 - 1] + sortedList[count / 2]) / 2.0;
382+
}
383+
else
384+
{
385+
return sortedList[count / 2];
386+
}
387+
}
271388
}
272389
}

0 commit comments

Comments
 (0)