November 18, 2020

Conditional Null Operator ?.

expression1?.expression2  => expression1 != null ? expression2 : null
This expression evaluates to: expression2, if expression1 is Not-Null. else, it evaluates to null
What happens when a conditional operator is inside a boolean expression?
void Main()
{
    A a = new A();
    if (a?.BVal?.Value == 4)
	{
	    Console.WriteLine("boolean expression returns true");
	}
	else
	{
		Console.WriteLine("boolean expression returns false"); // This line is output
	}
}

public class B
{
   public int Value { get; set; }
}

public class A
{
   public B BVal { get; set; }
}
A related operator is ??=
variable ??= expression // if (variable is null) { variable = expression; }

September 29, 2020

Extension methods to Convert Strings to Various Fundamental types

These extension methods converts strings to various types (int, bool, float)
When the string is invalid they return a fallback value.
public static class StringParserExtensions
{
	public static int ParseInt(this string part, int fallbackValue = 0)
	{
		if (!int.TryParse(part.Trim(), out int anInt))
		{
			anInt = fallbackValue;
		}
		return anInt;
	}

	public static bool ParseBool(this string part, bool fallbackValue = false)
	{
		if (!bool.TryParse(part.Trim(), out bool aBool))
		{
			aBool = fallbackValue;
		}
		return aBool;
	}

	public static float ParseFloat(this string part, float fallbackValue = 0.0f)
	{
		if (!float.TryParse(part.Trim(), out float number))
		{
			number = fallbackValue;
		}
		return number;
	}
}
Example usage:
  stationLocation.StationId = parts[0].ParseInt(0); 

Linq GroupBy/ToLookUp

These are Linq methods to group elements in a sequence. ToLookup() is the same as GroupBy(); the only differences are that GroupBy execution is deferred, whereas ToLookup() execution is immediate and also ToLookup() is not available in linq query form only the method form.

Note that the resultant group object exposes itself in 2 ways:

1. A 'Key' property exposes a value which all the items in the group have in common (this could be a calculated value)

2. The group object itself is an IEnumerable of the items in the group.

This operation will iterate over all the items in the enumeration. Works well with the Linq method "ToDictionary()" to convert a sequence into a dictionary.

Here is an example:

// Read StationLocationRaw(s) (int StationId and string Location)
// from the Station Locations file
public class StationLocationReader 
{
    FileInfo fi;
    public StationLocationReader(string path)
    {
        fi = new FileInfo(path);
    }

    public IDictionary<int, string> StationLocationsByStationId()
    {
        var stationLocationsDictionary =
            ReadStationLocations(). // returns StationLocationRaw(s)
                                    // (int StationId and string Location)
            GroupBy(s => s.StationId).
            ToDictionary(g => g.Key, g => g.Single().Location ?? "");
        return stationLocationsDictionary;
    }

    public IEnumerable<StationLocationRaw> ReadStationLocations()
    {
        Debug.Assert(fi.Exists);
        var stationLocations = fi.
            ReadLines().
            Skip(1). // Skip the header line
            Select(line => ExtractStationLocation(line));
        return stationLocations;
    }

    private StationLocationRaw ExtractStationLocation(string line)
    {
        var stationLocation = new StationLocationRaw();
        var parts = line.Split('\t');
        if (parts.Length >= 2)
        {
            // ParseInt is an extension method to convert
            // to a string to an integer, returning 0 
            // when the string is invalid.
            stationLocation.StationId = parts[0].Trim().ParseInt(0); 
            stationLocation.Location = parts[1].Trim();
        }
        return stationLocation;
    }
}

February 9, 2020

Sample Regular Expression

[TestMethod]
public void ParseVideoFileForDateTime2Test()
{
    var filePath = @"D:\Temp\Nokia 6\Photos\OpenCamera\VID_20190623_085427.mp4";
    var fileNameSansExt = Path.GetFileNameWithoutExtension(filePath);

    var dateTimeFormat = "yyyyMMdd_HHmmss";
    var result = string.Empty;
    Regex regExp = null;
    CalcRegExpForDateTimeFormat(dateTimeFormat, out regExp);
    if (regExp != null)
    {
        result = regExp.MatchFirstRegExp(fileNameSansExt);
    }
    Assert.IsNotNull(regExp);
    Assert.IsTrue(result.Length == 15);
    Assert.IsTrue(result == "20190623_085427");
}

private static bool CalcRegExpForDateTimeFormat(
    string dateTimeFormat, out Regex regExp)
{
    var res = false;
    regExp = null;
    var newRegex = new StringBuilder(dateTimeFormat.Length);
    foreach (var ch in dateTimeFormat)
    {
        if ((ch == 'y') || (ch == 'M') || (ch == 'd') ||
            (ch == 'H') || (ch == 'm') || (ch == 's'))
        {
            newRegex.Append(@"\d");
        }
        else
        {
            newRegex.Append(ch);
        }
    }
    var regexStr = newRegex.ToString();
    res = (regexStr.Length > 0);
    if (res)
    {
        regExp = new Regex(regexStr, RegexOptions.Compiled);
    }
    return res;
}

public static class RegularExpressionExtensions
{
/// <summary>
/// Match the first occurance of a regular expression in a target string.
/// </summary>
/// <param name="target">string to search</param>
/// <param name="regexp">regular expression object to use for the
/// search</param>
/// <returns>first match of the regular expression otherwise an 
/// empty string if there is no match</returns>
  public static string MatchFirstRegExp(this Regex reg, string target)
  {
    var result = "";
    MatchCollection mc = reg.Matches(target);
    if (mc.Count > 0)
    {
        result = mc[0].Value;
    }
    return result;
  }
}