diff --git a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj index 666fc5a..587d005 100644 --- a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj +++ b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/SharpHelpers/SharpHelpers/BooleanHelper.cs b/SharpHelpers/SharpHelpers/BooleanHelper.cs index c2049c4..7e74f19 100755 --- a/SharpHelpers/SharpHelpers/BooleanHelper.cs +++ b/SharpHelpers/SharpHelpers/BooleanHelper.cs @@ -1,5 +1,7 @@ // (c) 2019 SharpCoding // This code is licensed under MIT license (see LICENSE.txt for details) +using System; + namespace SharpCoding.SharpHelpers { public static class BooleanHelper @@ -49,5 +51,51 @@ public static string ToStringValues(this bool? value, string trueValue, string f { return value.HasValue ? (value.Value ? trueValue : falseValue) : falseValue; } + + /// + /// Negates the instance boolean value. + /// + /// The boolean value to negate. + /// Returns the negated boolean value. + public static bool Not(this bool instance) + { + return !instance; + } + + /// + /// Determines if the instance is true and executes the specified action if true. + /// + /// The boolean value to evaluate. + /// The action to execute if the boolean value is true. + public static void IfTrue(this bool instance, Action action) + { + if (instance) + { + action?.Invoke(); + } + } + + /// + /// Determines if the instance is false and executes the specified action if false. + /// + /// The boolean value to evaluate. + /// The action to execute if the boolean value is false. + public static void IfFalse(this bool instance, Action action) + { + if (!instance) + { + action?.Invoke(); + } + } + + /// + /// Returns the boolean value as an integer (1 for true, 0 for false). + /// + /// The boolean value to convert. + /// 1 if true, 0 if false. + public static int ToInt(this bool instance) + { + return instance ? 1 : 0; + } } } diff --git a/SharpHelpers/SharpHelpers/DataTableHelper.cs b/SharpHelpers/SharpHelpers/DataTableHelper.cs index f1fe5e2..6df897a 100644 --- a/SharpHelpers/SharpHelpers/DataTableHelper.cs +++ b/SharpHelpers/SharpHelpers/DataTableHelper.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Linq; using System.Reflection; namespace SharpCoding.SharpHelpers @@ -51,6 +52,139 @@ public static DataTable SetColumnsOrder(this DataTable table, string[] columnNam list.Add(objClass); } return list; + } + + /// + /// Converts the DataTable to a CSV format string. + /// + /// + /// + /// + public static string ToCsv(this DataTable table, string delimiter = ",") + { + if (table == null) throw new ArgumentNullException(nameof(table)); + + var csv = new List(); + var headers = string.Join(delimiter, table.Columns.Cast().Select(c => c.ColumnName)); + csv.Add(headers); + + foreach (DataRow row in table.Rows) + { + var line = string.Join(delimiter, row.ItemArray.Select(field => field?.ToString())); + csv.Add(line); + } + return string.Join(Environment.NewLine, csv); + } + + /// + /// Adds a new column to the DataTable with the specified default value. + /// + /// + /// + /// + /// + public static void AddColumn(this DataTable table, string columnName, T defaultValue = default) + { + if (table == null) throw new ArgumentNullException(nameof(table)); + + var column = new DataColumn(columnName, typeof(T)) { DefaultValue = defaultValue }; + table.Columns.Add(column); + foreach (DataRow row in table.Rows) + { + row[columnName] = defaultValue; + } + } + + /// + /// Merges multiple DataTables with the same schema into one. + /// + /// + /// + /// + public static DataTable MergeTables(IEnumerable tables) + { + if (tables == null) throw new ArgumentNullException(nameof(tables)); + + var resultTable = tables.First().Clone(); + foreach (var table in tables) + { + if (!AreSchemasCompatible(resultTable, table)) + throw new ArgumentException("Tables have incompatible schemas."); + + foreach (DataRow row in table.Rows) + { + resultTable.ImportRow(row); + } + } + return resultTable; + } + + private static bool AreSchemasCompatible(DataTable table1, DataTable table2) + { + if (table1.Columns.Count != table2.Columns.Count) return false; + + for (int i = 0; i < table1.Columns.Count; i++) + { + if (table1.Columns[i].ColumnName != table2.Columns[i].ColumnName || + table1.Columns[i].DataType != table2.Columns[i].DataType) + return false; + } + return true; + } + + /// + /// Filters the rows in the DataTable based on a predicate. + /// + /// + /// + /// + public static DataTable Filter(this DataTable table, Func predicate) + { + if (table == null) throw new ArgumentNullException(nameof(table)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + + var filteredTable = table.Clone(); + foreach (DataRow row in table.AsEnumerable().Where(predicate)) + { + filteredTable.ImportRow(row); + } + return filteredTable; + } + + /// + /// Checks if the DataTable is empty (contains no rows). + /// + /// + /// + public static bool IsEmpty(this DataTable table) + { + if (table == null) throw new ArgumentNullException(nameof(table)); + + return table.Rows.Count == 0; + } + + /// + /// Removes duplicate rows based on specified columns. + /// + /// + /// + /// + public static DataTable RemoveDuplicates(this DataTable table, params string[] columnNames) + { + if (table == null) throw new ArgumentNullException(nameof(table)); + + var distinctTable = table.Clone(); + var uniqueRows = new HashSet(); + + foreach (DataRow row in table.Rows) + { + var key = string.Join("|", columnNames.Select(c => row[c]?.ToString() ?? "")); + if (uniqueRows.Add(key)) + { + distinctTable.ImportRow(row); + } + } + return distinctTable; } } } diff --git a/SharpHelpers/SharpHelpers/EnumerableHelper.cs b/SharpHelpers/SharpHelpers/EnumerableHelper.cs index 8ffd44f..eee914d 100644 --- a/SharpHelpers/SharpHelpers/EnumerableHelper.cs +++ b/SharpHelpers/SharpHelpers/EnumerableHelper.cs @@ -268,5 +268,122 @@ public static int Sum(this IEnumerable source, Func selector) return source.Select(selector).Sum(); } + + /// + /// Returns the maximum element based on a given selector function. + /// + /// + /// + /// + /// + /// + /// + public static TSource MaxBy(this IEnumerable source, Func selector) + where TKey : IComparable + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (selector == null) throw new ArgumentNullException(nameof(selector)); + + return source.Aggregate((maxItem, nextItem) => selector(nextItem).CompareTo(selector(maxItem)) > 0 ? nextItem : maxItem); + } + + /// + /// Returns the minimum element based on a given selector function. + /// + /// + /// + /// + /// + /// + /// + public static TSource MinBy(this IEnumerable source, Func selector) + where TKey : IComparable + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (selector == null) throw new ArgumentNullException(nameof(selector)); + + return source.Aggregate((minItem, nextItem) => selector(nextItem).CompareTo(selector(minItem)) < 0 ? nextItem : minItem); + } + + /// + /// Finds the index of the first element that satisfies a given predicate. + /// + /// + /// + /// + /// + /// + public static int FindIndex(this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + + int index = 0; + foreach (var item in source) + { + if (predicate(item)) return index; + index++; + } + return -1; + } + + /// + /// Checks if the source contains any of the specified items. + /// + /// + /// + /// + /// + /// + public static bool ContainsAny(this IEnumerable source, params T[] items) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (items == null) throw new ArgumentNullException(nameof(items)); + + var set = new HashSet(items); + return source.Any(set.Contains); + } + + /// + /// Checks if the source contains all of the specified items. + /// + /// + /// + /// + /// + /// + public static bool ContainsAll(this IEnumerable source, params T[] items) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (items == null) throw new ArgumentNullException(nameof(items)); + + var set = new HashSet(source); + return items.All(set.Contains); + } + + /// + /// Returns the median of a sequence of numbers. + /// + /// + /// + /// + public static double Median(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + var sortedList = source.OrderBy(n => n).ToList(); + int count = sortedList.Count; + if (count == 0) + throw new InvalidOperationException("The source sequence is empty."); + + if (count % 2 == 0) + { + return (sortedList[count / 2 - 1] + sortedList[count / 2]) / 2.0; + } + else + { + return sortedList[count / 2]; + } + } } } diff --git a/SharpHelpers/SharpHelpers/NumericHelper.cs b/SharpHelpers/SharpHelpers/NumericHelper.cs index 3c07d98..76ca8f4 100644 --- a/SharpHelpers/SharpHelpers/NumericHelper.cs +++ b/SharpHelpers/SharpHelpers/NumericHelper.cs @@ -7,10 +7,10 @@ namespace SharpCoding.SharpHelpers public static class NumericHelper { /// - /// check if the int number that invokes the method is odd + /// Checks if the integer number is odd. /// - /// - /// + /// The integer number to check. + /// True if the number is odd; otherwise, false. public static bool IsOdd(this int numberToCheck) { var restOfDivision = (numberToCheck % 2); @@ -18,20 +18,20 @@ public static bool IsOdd(this int numberToCheck) } /// - /// check if the int number that invokes the method is even + /// Checks if the integer number is even. /// - /// - /// + /// The integer number to check. + /// True if the number is even; otherwise, false. public static bool IsEven(this int numberToCheck) { return !numberToCheck.IsOdd(); } /// - /// check if the int number that invokes the method is prime + /// Checks if the integer number is prime. /// - /// - /// + /// The integer number to check. + /// True if the number is prime; otherwise, false. public static bool IsPrime(this int numberToCheck) { var limit = Math.Ceiling(Math.Sqrt(numberToCheck)); @@ -44,5 +44,89 @@ public static bool IsPrime(this int numberToCheck) } return true; } + + /// + /// Calculates the factorial of the integer number. + /// + /// The integer number to calculate the factorial for. + /// The factorial of the number. + /// Thrown when the number is negative. + public static long Factorial(this int number) + { + if (number < 0) throw new ArgumentException("Number must be non-negative."); + return number <= 1 ? 1 : number * Factorial(number - 1); + } + + /// + /// Computes the Greatest Common Divisor (GCD) of two integers. + /// + /// The first integer. + /// The second integer. + /// The greatest common divisor of the two numbers. + public static int GCD(this int a, int b) + { + while (b != 0) + { + int temp = b; + b = a % b; + a = temp; + } + return a; + } + + /// + /// Computes the Least Common Multiple (LCM) of two integers. + /// + /// The first integer. + /// The second integer. + /// The least common multiple of the two numbers. + public static int LCM(this int a, int b) + { + return Math.Abs(a * b) / a.GCD(b); + } + + /// + /// Checks if the integer number is negative. + /// + /// The integer number to check. + /// True if the number is negative; otherwise, false. + public static bool IsNegative(this int number) + { + return number < 0; + } + + /// + /// Checks if the integer number is positive. + /// + /// The integer number to check. + /// True if the number is positive; otherwise, false. + public static bool IsPositive(this int number) + { + return number > 0; + } + + /// + /// Clamps the integer number within the specified range. + /// + /// The integer number to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value within the specified range. + public static int Clamp(this int value, int min, int max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + /// + /// Returns the absolute value of the integer number. + /// + /// The integer number to get the absolute value for. + /// The absolute value of the number. + public static int Abs(this int number) + { + return Math.Abs(number); + } } } diff --git a/SharpHelpers/SharpHelpers/ObjectHelper.cs b/SharpHelpers/SharpHelpers/ObjectHelper.cs index 35e8582..b1c4e16 100644 --- a/SharpHelpers/SharpHelpers/ObjectHelper.cs +++ b/SharpHelpers/SharpHelpers/ObjectHelper.cs @@ -7,29 +7,37 @@ namespace SharpCoding.SharpHelpers { public static class ObjectHelper { + private static readonly Type[] SystemTypes = + { + typeof(Enum), + typeof(string), + typeof(decimal), + typeof(DateTime), + typeof(DateTimeOffset), + typeof(TimeSpan), + typeof(Guid) + }; + /// - /// This method verifies that the passed type is a .Net Type + /// Checks if the given type is a system type (e.g., primitive, value types, common system types). /// - /// - /// + /// The type to check. + /// True if the type is a system type; otherwise, false. public static bool IsSystemType(this Type type) { - return - type.IsValueType || - type.IsPrimitive || - new[] - { - typeof(Enum), - typeof(string), - typeof(decimal), - typeof(DateTime), - typeof(DateTimeOffset), - typeof(TimeSpan), - typeof(Guid) - }.Contains(type) || - Convert.GetTypeCode(type) != TypeCode.Object || - (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && - IsSystemType(type.GetGenericArguments()[0])); + if (type.IsValueType || type.IsPrimitive) + return true; + + // Handle Nullable types + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + return IsSystemType(type.GetGenericArguments()[0]); + + // Check against predefined system types + if (SystemTypes.Contains(type)) + return true; + + // Check the type code to cover other system types + return Convert.GetTypeCode(type) != TypeCode.Object; } } } diff --git a/SharpHelpers/SharpHelpers/SharpHelpers.csproj b/SharpHelpers/SharpHelpers/SharpHelpers.csproj index 473433f..ce0c743 100644 --- a/SharpHelpers/SharpHelpers/SharpHelpers.csproj +++ b/SharpHelpers/SharpHelpers/SharpHelpers.csproj @@ -13,12 +13,16 @@ SharpCoding, Helpers, C# SharpHelpers is a collections of some handy code packages and tutorials to make developer's life easier. - 1.0.1 + 2.2.0 8 ico.png + https://github.com/sharpcode-it/SharpHelpers + README.md + 2.2.0 + 2.2.0 @@ -26,6 +30,10 @@ True + + True + \ + True @@ -37,6 +45,6 @@ - + diff --git a/SharpHelpers/SharpHelpers/StreamHelper.cs b/SharpHelpers/SharpHelpers/StreamHelper.cs index 33e7158..41b8eb9 100644 --- a/SharpHelpers/SharpHelpers/StreamHelper.cs +++ b/SharpHelpers/SharpHelpers/StreamHelper.cs @@ -106,11 +106,8 @@ public static Stream FromString(this string stringToConvert) /// /// public static Stream FromArray(this byte[] arrayToConvert) - { - var stream = new MemoryStream(arrayToConvert.Length); - stream.Write(arrayToConvert, 0, arrayToConvert.Length); - stream.Position = 0; - return stream; + { + return new MemoryStream(arrayToConvert, writable: false); } /// @@ -121,9 +118,9 @@ public static Stream FromArray(this byte[] arrayToConvert) /// public static bool ToFile(this Stream stream, string fileName) { - return ToFile(stream, fileName, true, Encoding.Default); - } - + return ToFile(stream, fileName, true); + } + /// /// Writes a stream to file /// @@ -131,64 +128,41 @@ public static bool ToFile(this Stream stream, string fileName) /// Name of the file. /// If set to true override existing file. /// True if successful - public static bool ToFile(this Stream stream, string fileName, bool overrideExisting) - { - return ToFile(stream, fileName, overrideExisting, Encoding.Default); - } - - /// - /// Writes a stream to file - /// - /// - /// - /// - /// - /// - public static bool ToFile(this Stream stream, string fileName, bool overrideExisting, Encoding encoding) - { - //Check if the sepcified file exists - if (File.Exists(fileName)) - if (overrideExisting) - File.Delete(fileName); - else - throw new AccessViolationException("File already exists"); - - try - { - //Create the file if it does not exist and open it - stream.Position = 0; - using (var fileStream = new FileStream(fileName, FileMode.CreateNew, FileAccess.ReadWrite)) - { - var reader = new BinaryReader(stream); - var writer = new BinaryWriter(fileStream, encoding); - writer.Write(reader.ReadBytes((int)stream.Length)); - writer.Flush(); - writer.Close(); - reader.Close(); - fileStream.Close(); - return true; - } - } - catch - { - return false; - } - } - + public static bool ToFile(this Stream stream, string fileName, bool overrideExisting) + { + try + { + if (File.Exists(fileName)) + { + if (overrideExisting) + File.Delete(fileName); + else + throw new IOException("File already exists"); + } + + stream.Position = 0; + using (var fileStream = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write)) + { + stream.CopyTo(fileStream); + } + + return true; + } + catch (IOException) + { + return false; + } + } + /// /// Loads a stream from a file /// /// /// - public static Stream FromFile(string fileName) - { - var fileStream = File.OpenRead(fileName); - var fileLength = (int)fileStream.Length; - var fileBytes = new byte[fileLength]; - fileStream.Read(fileBytes, 0, fileLength); - fileStream.Close(); - fileStream.Dispose(); - return FromArray(fileBytes); + public static Stream FromFile(this string fileName) + { + byte[] fileBytes = File.ReadAllBytes(fileName); + return FromArray(fileBytes); } } } diff --git a/SharpHelpers/SharpHelpers/XmlDocumentHelper.cs b/SharpHelpers/SharpHelpers/XmlDocumentHelper.cs index 561b41d..04c138d 100755 --- a/SharpHelpers/SharpHelpers/XmlDocumentHelper.cs +++ b/SharpHelpers/SharpHelpers/XmlDocumentHelper.cs @@ -1,6 +1,7 @@ // (c) 2019 SharpCoding // This code is licensed under MIT license (see LICENSE.txt for details) using System; +using System.IO; using System.Text; using System.Xml; using System.Xml.Linq; @@ -10,10 +11,11 @@ namespace SharpCoding.SharpHelpers public static class XmlDocumentHelper { /// - /// Return a XmlDocument from a XDocument + /// Converts an XDocument to an XmlDocument. /// - /// - /// + /// The XDocument to convert. + /// The converted XmlDocument. + /// Thrown when xDocument is null. public static XmlDocument ToXmlDocument(this XDocument xDocument) { if (xDocument == null) throw new ArgumentNullException(nameof(xDocument)); @@ -27,10 +29,11 @@ public static XmlDocument ToXmlDocument(this XDocument xDocument) } /// - /// Return a XDocument from a XmlDocument + /// Converts an XmlDocument to an XDocument. /// - /// - /// + /// The XmlDocument to convert. + /// The converted XDocument. + /// Thrown when xmlDocument is null. public static XDocument ToXDocument(this XmlDocument xmlDocument) { if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); @@ -43,10 +46,11 @@ public static XDocument ToXDocument(this XmlDocument xmlDocument) } /// - /// Return a XmlDocument from a XElement + /// Converts an XElement to an XmlDocument. /// - /// - /// + /// The XElement to convert. + /// The converted XmlDocument. + /// Thrown when xElement is null. public static XmlDocument ToXmlDocument(this XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); @@ -61,5 +65,74 @@ public static XmlDocument ToXmlDocument(this XElement xElement) doc.LoadXml(sb.ToString()); return doc; } + + /// + /// Finds the first child node with the specified name. + /// + /// The XmlDocument to search. + /// The name of the node to find. + /// The first matching XmlNode, or null if not found. + public static XmlNode FindFirstChild(this XmlDocument xmlDocument, string nodeName) + { + if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); + if (string.IsNullOrWhiteSpace(nodeName)) throw new ArgumentException("Node name cannot be null or empty.", nameof(nodeName)); + + return xmlDocument.SelectSingleNode($"//{nodeName}"); + } + + /// + /// Adds a new child element with the specified name and value to the given parent node. + /// + /// The XmlDocument to modify. + /// The parent node to which the new element will be added. + /// The name of the new element. + /// The value of the new element. + /// The newly created XmlElement. + public static XmlElement AddChildElement(this XmlDocument xmlDocument, XmlNode parentNode, string elementName, string elementValue) + { + if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); + if (parentNode == null) throw new ArgumentNullException(nameof(parentNode)); + if (string.IsNullOrWhiteSpace(elementName)) throw new ArgumentException("Element name cannot be null or empty.", nameof(elementName)); + + var newElement = xmlDocument.CreateElement(elementName); + newElement.InnerText = elementValue; + parentNode.AppendChild(newElement); + return newElement; + } + + /// + /// Converts the XmlDocument to a formatted string. + /// + /// The XmlDocument to convert. + /// A string representation of the XmlDocument. + public static string ToFormattedString(this XmlDocument xmlDocument) + { + if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); + + using (var stringWriter = new StringWriter()) + { + using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true })) + { + xmlDocument.Save(xmlWriter); + } + return stringWriter.ToString(); + } + } + + /// + /// Removes the specified node from the XmlDocument. + /// + /// The XmlDocument to modify. + /// The node to remove. + public static void RemoveNode(this XmlDocument xmlDocument, XmlNode node) + { + if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); + if (node == null) throw new ArgumentNullException(nameof(node)); + + if (node.ParentNode != null) + { + node.ParentNode.RemoveChild(node); + } + } } }