Handle variable number of out parameters with less code duplication in C# - c#

I'm trying to write a function that populates strings with the contents of an array, or sets them to null. The number of strings is can vary and I don't want to add requirements like them all being part of the same array or class.
In C# you cannot combine param and out. Therefore the only way to do this seems to be to overload the method like this:
public void ParseRemainders(string[] remainders, out string p1)
{
p1 = null;
if ((remainders != null) && (remainders.Length > 0))
p1 = remainders[0];
}
public void ParseRemainders(string[] remainders, out string p1, out string p2)
{
p1 = null;
p2 = null;
if (remainders != null)
{
ParseRemainders(remainders, out p1);
if (remainders.Length > 1)
p2 = remainders[1];
}
}
public void ParseRemainders(string[] remainders, out string p1, out string p2, out string p3)
{
p1 = null;
p2 = null;
p3 = null;
if (remainders != null)
{
ParseRemainders(remainders, out p1, out p2);
if (remainders.Length > 2)
p3 = remainders[2];
}
}
.... and on forever ....
How can I avoid all this code duplication, ideally accepting an arbitrary number of parameters?
Edit: This is useful because you could do, say, ParseRemainders(remainders, out inputFileName, out outputFileName, out configFileName) and then avoid having to manually do
if (remainder.Length > 0) inputFileName = remainder[0];
if (remainder.Length > 1) outputFileName = remainder[1];
if (remainder.Length > 2) configFileName = remainder[2];
...
Sorry if this wasn't clear, I had a specific goal in mind which I why I didn't simply return a List<>.
Conclusion: Thanks to Botond Balázs for the answer, particularly the hint that this is called "array destructuring". As they point out, and as this question confirms, it is not possible in the current version of C#: Destructuring assignment - object properties to variables in C#

I would take a different approach than any of the answers so far.
static class Extensions {
public static SafeArrayReader<T> MakeSafe<T>(this T[] items)
{
return new SafeArrayReader<T>(items);
}
}
struct SafeArrayReader<T>
{
private T[] items;
public SafeArrayReader(T[] items) { this.items = items; }
public T this[int index]
{
get
{
if (items == null || index < 0 || index >= items.Length)
return default(T);
return items[index];
}
}
}
There, now you have an array that gives you a default value instead of throwing:
var remainder = GetRemainders().MakeSafe();
var input = remainder[0];
var output = remainder[1];
var config = remainder[2];
Easy peasy. You have a problem with the semantics of a data type? Make a better data type that encapsulates the desired semantics.

If I understand you correctly, your use case would look like this:
var remainders = new[] { "a", "b", "c" };
string a, b, c;
ParseRemainders(remainders, a, b, c); // after this, a == "a", b == "b" and c == "c"
The feature you want to have in C# is called array destructuring, like in JavaScript:
var remainders = ["a", "b", "c"];
var [a, b, c] = remainders; // after this, a == "a", b == "b" and c == "c"
Unfortunately, as far as I know,
this cannot be solved in a general way using C#.
C# 7 will have tuple destructuring though.

Well, you can change your method to something like
public IEnumerable<string> ParseRemainders(string[] remainders)
{
var result = new List<string>();
///... your logic here, fill list with your strings according to your needs
return result;
}

Andys approach is fine but i'd return a string[] because it should have the same size as the input array and also return null if the input array was null:
public string[] ParseRemainders(string[] remainders)
{
if(remainders == null) return null;
var parsed = new string[remainders.Length];
for(int i = 0; i < remainders.Length; i++)
parsed[i] = ParseRemainder(remainders[i]);
return parsed;
}
To clarify what ParseRemainder(different method for a single string) does:
public string ParseRemainder(string remainder)
{
// parsing logic here...
return "the parsing result of remainder";
}

For completeness, this is how you can do this kind of thing in C#7 (Visual Studio 2017):
string[] test = { "One", "Two", "Three", "Four", "Five" };
var (a, b, c) = (test[0], test[2], test[4]);
Debug.Assert(a == "One");
Debug.Assert(b == "Three");
Debug.Assert(c == "Five");
The important line here is var (a, b, c) = (test[0], test[2], test[4]); which shows you the shorthand way of assigning several different variables from some elements of an array.
However, this doesn't help with the assigning of null if the array isn't long enough. You could get around that problem by writing a helper class:
public sealed class ElementsOrNull<T> where T: class
{
readonly IList<T> array;
public ElementsOrNull(IList<T> array)
{
this.array = array;
}
public T this[int index]
{
get
{
if (index < array.Count)
return array[index];
return null;
}
}
}
And then:
string[] test = { "One", "Two", "Three", "Four", "Five" };
var t = new ElementsOrNull<string>(test);
var (a, b, c) = (t[0], t[2], t[6]);
Debug.Assert(a == "One");
Debug.Assert(b == "Three");
Debug.Assert(c == null);
But I'm sure most people (myself included) will think that's more trouble than it's worth.

I think this gets pretty close to what you want. It doesn't need C# 7, works with any data element type, and isn't limited to arrays. You may want to pick better names than ValueReader/ReadValue, though.
static class Extensions
{
public static ValueReader<T> ReadValue<T>(this IEnumerable<T> source, out T value)
{
var result = new ValueReader<T>(source);
result.ReadValue(out value);
return result;
}
}
class ValueReader<T>
{
IEnumerator<T> _enumerator;
public ValueReader(IEnumerable<T> source)
{
if (source == null) source = new T[0];
_enumerator = source.GetEnumerator();
}
public ValueReader<T> ReadValue(out T value)
{
bool hasNext = _enumerator.MoveNext();
value = hasNext ? _enumerator.Current : default(T);
return this;
}
}
static class TestApp
{
public static void Main()
{
var remainders = new string[] { "test1", "test2", "test3" };
string inputFileName, outputFileName, configFileName, willBeSetToNull;
remainders
.ReadValue(out inputFileName)
.ReadValue(out outputFileName)
.ReadValue(out configFileName)
.ReadValue(out willBeSetToNull);
}
}

Just use an index in the array, eg:
remainers[0]; //same as p1
remainers[1]; //same as p2
remainers[2]; //same as p3

From your description I'm guessing your use case would be something similar to:
public void SomeMethod( ... )
{
string p1;
string p2;
....
ParseRemainders(string[] remainders, out string p1, out string p2);
...
}
public void SomeOtherMethod( ... )
{
string p1;
string p2;
string p3;
....
ParseRemainders(string[] remainders, out string p1, out string p2, out string p3);
...
}
You don't need to return strings this way. As already pointed out in other answers / comments, you can simply return an array of strings:
string[] ParseRemainders(string[] remainders)
{
var result = new string[remainder.Length];
result[0] = //whatever p1 would be
result[1] = //whatever p2 would be
//etc.
}
And you would use it like this:
public void SomeMethod( ... )
{
....
var parsed = ParseRemainders(string[] remainders);
string p1 = parsed[0];
string p2 = parsed[1];
....
}
That looks a lot better.

It feels like you're trying to over-complicate a simple null check, just go back to basics and keep it simple:
public string GetRemainder(string[] remainders, int index)
{
if ((remainders != null) && (remainders.Length > index))
return remainders[index];
return null;
}
Usage:
var inputFileName = GetRemainder(remainder, 0);
var outputFileName = GetRemainder(remainder, 1);
var configFileName = GetRemainder(remainder, 2);

Related

Sort numbers then strings C#

I want to this list
A
B
C
111
11
123
1
42
5
To be sorted
1
5
11
42
111
123
A
B
C
By default, it sorts numbers like strings (So, it goes 1,11,111,123,42,5),
But I want to sort numbers like numbers, and than strings that are not numbers.
Is there clean solution to sort it like above?
It is a list of objects, and object has several properties, one of which is a this string.
This will work for most use cases, but may have odd results if the string starts with control characters, string like "\tabc" will come before the integers:
list.OrderBy(x=>int.TryParse(x, out var dummy) ? dummy.ToString("D10") : x);
or for versions of C# prior to 7:
list.OrderBy(x=> { int dummy; return int.TryParse(x, out dummy) ? dummy.ToString("D10") : x;} );
What you want is called Natural sort.
I once wrote some code for that:
public static class NaturalCompare
{
public static int Compare(string first, string second, StringComparison comparison = StringComparison.Ordinal)
{
if (string.Compare(first, second, comparison) == 0)
{
return 0;
}
if (first == null)
{
return -1;
}
if (second == null)
{
return 1;
}
DateTime d1, d2;
if (DateTime.TryParse(first, out d1) && DateTime.TryParse(second, out d2))
{
return d1.CompareTo(d2);
}
var pos1 = 0;
var pos2 = 0;
int result;
do
{
bool isNum1, isNum2;
var part1 = GetNext(first, ref pos1, out isNum1);
var part2 = GetNext(second, ref pos2, out isNum2);
if (isNum1 && isNum2)
{
result = long.Parse(part1).CompareTo(long.Parse(part2));
}
else
{
result = String.Compare(part1, part2, comparison);
}
} while (result == 0 && pos1 < first.Length && pos2 < second.Length);
return result;
}
public static int CompareToNatural(this string first, string second, StringComparison comparison = StringComparison.Ordinal)
{
return Compare(first, second, comparison);
}
public static IOrderedEnumerable<TSource> OrderByNatural<TSource>(this IEnumerable<TSource> source, Func<TSource, string> keySelector)
{
return source.OrderBy(keySelector, new NatComparer());
}
public static IOrderedEnumerable<TSource> OrderByNaturalDescending<TSource>(this IEnumerable<TSource> source, Func<TSource, string> keySelector)
{
return source.OrderByDescending(keySelector, new NatComparer());
}
private sealed class NatComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return NaturalCompare.Compare(x, y);
}
}
private static string GetNext(string s, ref int index, out bool isNumber)
{
if (index >= s.Length)
{
isNumber = false;
return "";
}
isNumber = char.IsDigit(s[index]);
var start = index;
while (index < s.Length && char.IsDigit(s[index]) == isNumber)
{
index++;
}
return s.Substring(start, index - start);
}
}
I wrote this IComparer implementation a few months back to handle something like this. I think it will do what you want by default, though it is built to handle more complex cases where number/letter groups are separated by delimiters that also need to be sorted atomically. You should be able to adjust it to your needs.
public class SemanticComparer : IComparer<string>
{
private static Regex _splitter = new Regex("\\W+");
public int Compare(string x, string y)
{
string[] partsX = _splitter.Split(x);
string[] partsY = _splitter.Split(y);
int shortest = Math.Min(partsX.Length, partsY.Length);
for (int index = 0; index < shortest; index++)
{
int intX, intY;
int result;
if (int.TryParse(partsX[index], out intX) && int.TryParse(partsY[index], out intY))
{
result = intX.CompareTo(intY);
}
else
{
result = string.Compare(partsX[index], partsY[index], StringComparison.Ordinal);
}
if (result != 0)
{
return result;
}
}
return 0;
}
}
You can sort your list with it like this:
MyList.Sort(new SemanticComparer());
You could loop through all the values once, and use int.TryParse to separate them into two separate lists: one for the values where int.TryParse returned true (aka the numbers), and another list for the ones where it returned false (the non-numbers). Then you could sort these two lists separately, and concatenate their sorted results together at the end.
I haven't tested this code for performance, but you can solve this with a Comparer
public class ArrayItemComparer : IComparer<string>
{
public int Compare(string x, string y)
{
int xInt = 0, yInt = 0;
bool parseX = int.TryParse(x, out xInt);
bool parseY = int.TryParse(y, out yInt);
if (parseX && parseY)
{
return xInt.CompareTo(yInt);
}
else if (parseX)
{
return -1;
}
else if (parseY)
{
return 1;
}
else
{
return x.CompareTo(y);
}
}
}
I've created a solution for this. I've divided the list into two part then sort and concat. Please check below:
public List<ListItem> getSortedList()
{
int dummy = 0;
List<ListItem> list = new List<ListItem>();
list.Add(new ListItem() { Item = "A" });
list.Add(new ListItem() { Item = "B" });
list.Add(new ListItem() { Item = "C" });
list.Add(new ListItem() { Item = "111" });
list.Add(new ListItem() { Item = "11" });
list.Add(new ListItem() { Item = "123" });
list.Add(new ListItem() { Item = "1" });
list.Add(new ListItem() { Item = "42" });
list.Add(new ListItem() { Item = "5" });
var listNumber = list.Where(m => int.TryParse(m.Item, out dummy)).ToList().OrderBy(m => Convert.ToInt16(m.Item)).ToList();
var listString = list.Where(m => !int.TryParse(m.Item, out dummy)).ToList().OrderBy(m => m.Item).ToList();
var sortedList = listNumber.Concat(listString).ToList();
return sortedList;
}
You can run this in DotNetFiddle.
Assuming you start with a collection of strings, a simple comparer should do the job:
public class Comparer : IComparer<string>
{
public int Compare(string a, string b)
{
int ia = 0;
int ib = 0;
var aIsInt = int.TryParse(a,out ia);
var bIsInt = int.TryParse(b,out ib);
if (aIsInt == bIsInt)
{
if (aIsInt)
{
return ia.CompareTo(ib);
}
else
{
return a.CompareTo(b);
}
}
return aIsInt ? -1 : 1;
}
}
Here's a fiddle
With Regex.Replace in the "OrderBy" it's one (fairly) simple line of code. And note that the number "3" just has to be a number equal-to or larger than your longest string, so for anyone else increase as needed.
using System.Text.RegularExpressions;
string[] yourStrings = new string[] { "A", "B", "C", "111", "11", "123", "1", "42", "5" };
foreach (var item in yourStrings.OrderBy(x => Regex.Replace(x, #"\d+", i =>
i.Value.PadLeft(3, '0'))))
{
Response.Write(item + "\n");
}

C# Calculate Combinations from Multiple Lists of Custom Objects with No Repetition

So I have been struggling to find a way to create all combinations with no repetition from multiple lists containing custom objects. There are some additional constraints that make it a bit more challenging, of course.
Basically I am parsing a bunch of data from a .csv file that contains part information. This data is then passed on to a custom object and then those objects are added to lists based on their "group." (See code below)
So once the information has been parsed I now have 6 lists containing any number of elements. Now I need to generate all combinations between those 6 lists following these rules:
One object from groupA
Two objects from groupB (no repetition)
Three objects from groupC (no repetition)
One object from groupD
One object from groupE
One object from groupF
These objects are then used to create a ModuleFull object, and my overall end result should be a List<ModuleFull> containing all the combinations generated from the part lists.
I was able to figure out a way to do this using LINQ although I did not test it using lists of custom objects because I realized my lists all contain different numbers of elements.
So any help that I could get in coming up with a method to solve this using recursion would be greatly appreciated.
Here is the code parsing the data:
using (TextFieldParser parser = new TextFieldParser(#"c:\temp\test.csv"))
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
string[] fields = parser.ReadFields();
Part tempPart = new Part(fields[0], fields[2], fields[1], double.parse(fields[4]), long.parse(fields[3]));
allParts.Add(tempPart);
if (tempPart.group == "A")
{
aParts.Add(tempPart);
}
else if (tempPart.group == "B")
{
bParts.Add(tempPart);
}
else if (tempPart.group == "C")
{
cParts.Add(tempPart);
}
else if (tempPart.group == "D")
{
dParts.Add(tempPart);
}
else if (tempPart.group == "E")
{
eParts.Add(tempPart);
}
else if (tempPart.group == "F")
{
fParts.Add(tempPart);
}
}
Below are the two classes for the objects that fill the lists:
public class Part
{
public string idNum; //0 locations when being parsed
public string name; //2
public string group; //1
public double tolerance; //4
public long cost; //3
public Part(string id, string nm, string grp, double tol, long cst)
{
idNum = id;
name = nm;
group = grp;
tolerance = tol;
cost = cst;
}
}
public class ModuleFull
{
public Part groupA;
public Part groupBOne;
public Part groupBTwo;
public Part groupCOne;
public Part groupCTwo;
public Part groupCThree;
public Part groupD;
public Part groupE;
public Part groupF;
public ModuleFull(Part a, Part b1, Part b2, Part c1, Part c2, Part c3, Part d, Part e, Part f)
{
groupA = a;
groupBOne = b1;
groupBTwo = b2;
groupCOne = c1;
groupCTwo = c2;
groupCThree = c3;
groupD = d;
groupE = e;
groupF = f;
}
}
The code below uses a custom enumerator to get unique combinations. Very clean solution.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace IEnumerable_IEnumerator_Recursive
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
Parser parser = new Parser(FILENAME);
int level = 0;
List<Part> root = new List<Part>();
Part.Recurse(level, root);
}
}
public class Parser
{
public Boolean EndOfData = false;
public Parser(string filename)
{
StreamReader reader = new StreamReader(filename);
string inputLine = "";
while ((inputLine = reader.ReadLine()) != null)
{
inputLine = inputLine.Trim();
if (inputLine.Length > 0)
{
string[] fields = inputLine.Split(new char[] { ',' });
Part tempPart = new Part(fields[0], fields[1], fields[2], fields[3], fields[4]);
Part.allParts.Add(tempPart);
}
}
Part.MakeDictionary();
}
}
public class PartEnumerator : IEnumerator<List<Part>>
{
List<Part> parts = null;
public static SortedDictionary<string, int> maxCount = new SortedDictionary<string, int>() {
{"A", 1},
{"B", 2},
{"C", 3},
{"D", 1},
{"E", 1},
{"F", 1}
};
public int size = 0;
List<int> enumerators = null;
public PartEnumerator(string name, List<Part> parts)
{
this.parts = parts;
size = maxCount[name];
enumerators = new List<int>(new int[size]);
Reset();
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public List<Part> Current
{
get
{
try
{
List<Part> returnParts = new List<Part>();
foreach (int enumerator in enumerators)
{
returnParts.Add(parts[enumerator]);
}
return returnParts;
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public void Reset()
{
for (int count = 0; count < enumerators.Count; count++)
{
enumerators[count] = count;
}
}
public Boolean MoveNext()
{
Boolean moved = true;
int listSize = parts.Count;
int enumNumbers = enumerators.Count;
//only use enumerators up to the size of list
if (listSize < enumNumbers)
{
enumNumbers = listSize;
}
Boolean ripple = true;
int enumCounter = enumNumbers;
if (enumCounter > 0)
{
while ((ripple == true) && (--enumCounter >= 0))
{
ripple = false;
int maxCount = listSize - (enumNumbers - enumCounter);
if (enumerators[enumCounter] >= maxCount)
{
ripple = true;
}
else
{
for (int i = enumCounter; i < enumNumbers; i++)
{
if (i == enumCounter)
{
enumerators[i] += 1;
}
else
{
enumerators[i] = enumerators[i - 1] + 1;
}
}
}
}
if ((enumCounter <= 0) && (ripple == true))
{
moved = false;
}
}
return moved;
}
public void Dispose()
{
}
}
public class Part
{
public static List<Part> allParts = new List<Part>();
public static Dictionary<string, PartEnumerator> partDict = new Dictionary<string, PartEnumerator>();
public string idNum; //0 locations when being parsed
public string name; //2
public string group; //1
public double tolerance; //4
public long cost; //3
public Part()
{
}
public Part(string id, string nm, string grp, string tol, string cst)
{
idNum = id;
name = nm;
group = grp;
tolerance = double.Parse(tol);
cost = long.Parse(cst);
}
public static void MakeDictionary()
{
var listPartEnum = Part.allParts.GroupBy(x => x.name)
.Select(x => new { Key = x.Key, List = new PartEnumerator(x.Key, x.ToList()) });
foreach (var partEnum in listPartEnum)
{
partDict.Add(partEnum.Key, partEnum.List);
}
}
public static string[] NAMES = { "A", "B", "C", "D", "E", "F" };
public static void Recurse(int level, List<Part> results)
{
Boolean moved = true;
if (level < PartEnumerator.maxCount.Keys.Count)
{
//level is equivalent to names in the Part Enumerator dictionary A to F
string name = NAMES[level];
PartEnumerator enumerator = partDict[name];
enumerator.Reset();
while ((enumerator != null) && moved)
{
List<Part> allParts = new List<Part>(results);
allParts.AddRange((List<Part>)enumerator.Current);
int currentLevel = level + 1;
Recurse(currentLevel, allParts);
moved = enumerator.MoveNext();
}
}
else
{
string message = string.Join(",", results.Select(x => string.Format("[id:{0},name:{1}]", x.name, x.idNum)).ToArray());
Console.WriteLine(message);
}
}
}
}
I used following input file
1,A,X,0,0
2,A,X,0,0
3,A,X,0,0
4,A,X,0,0
5,A,X,0,0
1,B,X,0,0
2,B,X,0,0
3,B,X,0,0
4,B,X,0,0
5,B,X,0,0
1,C,X,0,0
2,C,X,0,0
3,C,X,0,0
4,C,X,0,0
5,C,X,0,0
1,D,X,0,0
2,D,X,0,0
3,D,X,0,0
4,D,X,0,0
5,D,X,0,0
1,E,X,0,0
2,E,X,0,0
3,E,X,0,0
4,E,X,0,0
5,E,X,0,0
1,F,X,0,0
2,F,X,0,0
3,F,X,0,0
4,F,X,0,0
5,F,X,0,0
This can be solved with two related methods. One is a method to generate all combinations of items from a list. This deals with your cases where you want more than one from a set, like group B and C. The other is a method to get you all the ways to combine one element from each list, which is otherwise known as a Cartesian product and is, in a way, a special case of the first method.
I recently wrote a library of combinatorial functions that includes both of these, so I can share my implementation with you. My library is on Github if you want to look at the source code, and can be installed from NuGet if you'd like. (The examples below are slightly simplified to fit your situation; in my fuller versions, the combinations method has different modes that allow you to specify whether it matters what order the output items are in, and whether it's allowed to use source items more than once. Neither are needed here, so they've been omitted.)
So, the first of those methods looks something like this:
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> source, int combinationSize)
{
if (combinationSize > source.Count())
{
return new List<IEnumerable<T>>();
}
if (source.Count() == 1)
{
return new[] { source };
}
var indexedSource = source
.Select((x, i) => new
{
Item = x,
Index = i
})
.ToList();
return indexedSource
.SelectMany(x => indexedSource
.OrderBy(y => x.Index != y.Index)
.Skip(1)
.OrderBy(y => y.Index)
.Skip(x.Index)
.Combinations(combinationSize - 1)
.Select(y => new[] { x }.Concat(y).Select(z => z.Item))
);
}
The second method is from a blog post by Eric Lippert (which was actually inspired by another StackOverflow question), and looks like this:
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
if (sequences == null)
{
throw new ArgumentNullException(nameof(sequences));
}
IEnumerable<IEnumerable<T>> emptyProduct = new IEnumerable<T>[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item }))
.Where(x => x.Any());
}
The two methods can be combined like this:
var groupA = new[] { "a", "aa", "aaa", "aaaa", "aaaaa" };
var groupB = new[] { "b", "bb", "bbb", "bbbb", "bbbbb" };
var groupC = new[] { "c", "cc", "ccc", "cccc", "ccccc" };
var groupD = new[] { "d", "dd", "ddd", "dddd", "ddddd" };
var groupE = new[] { "e", "ee", "eee", "eeee", "eeeee" };
var groupF = new[] { "f", "ff", "fff", "ffff", "fffff" };
var options = new[]
{
groupA.Combinations(1), // One object from groupA
groupB.Combinations(2), // Two objects from groupB (no repetition)
groupC.Combinations(3), // Three objects from groupC (no repetition)
groupD.Combinations(1), // One object from groupD
groupE.Combinations(1), // One object from groupE
groupF.Combinations(1) // One object from groupF
};
return options.CartesianProduct();
So, we generate the various ways of satisfying each of your sub-conditions first: one from this group, two from that group, etc. Then, we look at all ways of putting those together to form a group of subgroups. The result is an IEnumerable<IEnumerable<T>> where T is the type of what you started with - in this case, string, but for you it could be something else. You can then iterate over this and use each set to build your result type.
Be aware that, like many combinatorial problems, this can scale quite fast. For example, with my test data this returns 62,500 possible combinations.

Simple If and Else If Using Functions

Given this example snippet:
if(test == 5) {
var = 5;
var2 = 6;
}
else if(test == 6){
var = 30;
var2 = 25;
}
//...ect
How can I clean this up into a function? I thought of doing this:
void doStuff(int condition, int v1, int v2){
if(test == condition){
var = v1;
var2 = v2;
}
}
but then I would have to implement it like this:
doStuff(5,5,6);
doStuff(6,30,25);
//...ect
This would go through each function and check each if statement even if the first was evaluated to be true. This would not have the if, else if, else function unless I did something like this:
//Assuming doStuff returns a bool
if(doStuff(5,5,6)
else if(doStuff(6,30,25))
//...ect
Is there a better way to put functions inside conditional if / else if statements?
The switch approach is probably the best, but if the number of cases is huge, you can consider doing something funny like...
private static readonly Dictionary<int, int[]> _dic = new...
private void Foo()
{
var test = ...
var vals = this._dic[test];
a = vals[0];
b = vals[1];
...
}
And if not all variables are of type int, you can either use Tuple or your own structure to hold the information (+1 for naming values)
Edit: regarding your updated question about "better way to use methods in if-else":
doStuff(5,5,6) || doStuff(6,30,25) || ...
Will evaluate doStuff's from left to right until one returns true.
var conditionsMap = new Dictionary<int, Tuple<int, int>>();
conditionsMap.Add(5, new Tuple<int, int>(5, 6));
conditionsMap.Add(6, new Tuple<int, int>(30, 25));
foreach (var entry in conditionsMap)
{
var key = entry.Key;
var var1 = entry.Value.Item1;
var var2 = entry.Value.Item2;
Console.WriteLine("{0}\n{1}\n{3}", key, var1, var2);
}
In these situations, I tend to use an enum with custom attributes to define the different conditions. To me, that keeps it concise which aids in maintenance and also helps restrict the possible inputs to a predefined set.
Here's a full code sample (it can be pasted into LINQPad to test):
public class ValuesAttribute : Attribute
{
public int V1 { get; private set; }
public int V2 { get; private set; }
public ValuesAttribute(int v1, int v2)
{
V1 = v1;
V2 = v2;
}
}
public enum ValuesCase
{
[Values(5, 6)]
Five,
[Values(30, 25)]
Six
}
public void DoStuff(ValuesCase valuesCase)
{
// There are several ways to get an attribute value of an enum, but I like this one
ValuesAttribute values = valuesCase
.GetType()
.GetTypeInfo()
.GetDeclaredField(valuesCase.ToString())
.GetCustomAttribute<ValuesAttribute>();
if(values != null)
{
// Assign your variables or do whatever else you want here
Console.WriteLine(string.Join(", ", valuesCase.ToString(), values.V1, values.V2));
}
}
void Main()
{
DoStuff(ValuesCase.Five);
DoStuff(ValuesCase.Six);
}

Overloading Linq Except to allow custom struct with byte array

I am having a problem with a custom struct and overloading linq's except method to remove duplicates.
My struct is as follows:
public struct hashedFile
{
string _fileString;
byte[] _fileHash;
public hashedFile(string fileString, byte[] fileHash)
{
this._fileString = fileString;
this._fileHash = fileHash;
}
public string FileString { get { return _fileString; } }
public byte[] FileHash { get { return _fileHash; } }
}
Now, the following code works fine:
public static void test2()
{
List<hashedFile> list1 = new List<hashedFile>();
List<hashedFile> list2 = new List<hashedFile>();
hashedFile one = new hashedFile("test1", BitConverter.GetBytes(1));
hashedFile two = new hashedFile("test2", BitConverter.GetBytes(2));
hashedFile three = new hashedFile("test3", BitConverter.GetBytes(3));
hashedFile threeA = new hashedFile("test3", BitConverter.GetBytes(4));
hashedFile four = new hashedFile("test4", BitConverter.GetBytes(4));
list1.Add(one);
list1.Add(two);
list1.Add(threeA);
list1.Add(four);
list2.Add(one);
list2.Add(two);
list2.Add(three);
List<hashedFile> diff = list1.Except(list2).ToList();
foreach (hashedFile h in diff)
{
MessageBox.Show(h.FileString + Environment.NewLine + h.FileHash[0].ToString("x2"));
}
}
This code shows "threeA" and "four" just fine. But if I do the following.
public static List<hashedFile> list1(var stuff1)
{
//Generate a List here and return it
}
public static List<hashedFile> list2(var stuff2)
{
//Generate a List here and return it
}
List<hashedFile> diff = list1.except(list2);
"diff" becomes an exact copy of "list1". I should also mention that I am sending a byte array from ComputeHash from System.Security.Cryptography.MD5 to the byte fileHash in the list generations.
Any ideas on how to overload either the Except or GetHashCode method for linq to successfully exclude the duplicate values from list2?
I'd really appreciate it! Thanks!
~MrFreeman
EDIT: Here was how I was originally trying to use List<hashedFile> diff = newList.Except(oldList, new hashedFileComparer()).ToList();
class hashedFileComparer : IEqualityComparer<hashedFile>
{
public bool Equals(hashedFile x, hashedFile y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.FileString == y.FileString && x.FileHash == y.FileHash;
}
public int GetHashCode(hashedFile Hashedfile)
{
if (Object.ReferenceEquals(Hashedfile, null)) return 0;
int hashFileString = Hashedfile.FileString == null ? 0 : Hashedfile.FileString.GetHashCode();
int hashFileHash = Hashedfile.FileHash.GetHashCode();
int returnVal = hashFileString ^ hashFileHash;
if (Hashedfile.FileString.Contains("blankmusic") == true)
{
Console.WriteLine(returnVal.ToString());
}
return returnVal;
}
}
If you want the type to handle its own comparisons in Except the interface you need is IEquatable. The IEqualityComparer interface is to have another type handle the comparisons so it can be passed into Except as an overload.
This achieves what you want (assuming you wanted both file string and hash compared).
public struct hashedFile : IEquatable<hashedFile>
{
string _fileString;
byte[] _fileHash;
public hashedFile(string fileString, byte[] fileHash)
{
this._fileString = fileString;
this._fileHash = fileHash;
}
public string FileString { get { return _fileString; } }
public byte[] FileHash { get { return _fileHash; } }
public bool Equals(hashedFile other)
{
return _fileString == other._fileString && _fileHash.SequenceEqual(other._fileHash);
}
}
Here is an example in a working console application.
public class Program
{
public struct hashedFile : IEquatable<hashedFile>
{
string _fileString;
byte[] _fileHash;
public hashedFile(string fileString, byte[] fileHash)
{
this._fileString = fileString;
this._fileHash = fileHash;
}
public string FileString { get { return _fileString; } }
public byte[] FileHash { get { return _fileHash; } }
public bool Equals(hashedFile other)
{
return _fileString == other._fileString && _fileHash.SequenceEqual(other._fileHash);
}
}
public static void Main(string[] args)
{
List<hashedFile> list1 = GetList1();
List<hashedFile> list2 = GetList2();
List<hashedFile> diff = list1.Except(list2).ToList();
foreach (hashedFile h in diff)
{
Console.WriteLine(h.FileString + Environment.NewLine + h.FileHash[0].ToString("x2"));
}
Console.ReadLine();
}
private static List<hashedFile> GetList1()
{
hashedFile one = new hashedFile("test1", BitConverter.GetBytes(1));
hashedFile two = new hashedFile("test2", BitConverter.GetBytes(2));
hashedFile threeA = new hashedFile("test3", BitConverter.GetBytes(4));
hashedFile four = new hashedFile("test4", BitConverter.GetBytes(4));
var list1 = new List<hashedFile>();
list1.Add(one);
list1.Add(two);
list1.Add(threeA);
list1.Add(four);
return list1;
}
private static List<hashedFile> GetList2()
{
hashedFile one = new hashedFile("test1", BitConverter.GetBytes(1));
hashedFile two = new hashedFile("test2", BitConverter.GetBytes(2));
hashedFile three = new hashedFile("test3", BitConverter.GetBytes(3));
var list1 = new List<hashedFile>();
list1.Add(one);
list1.Add(two);
list1.Add(three);
return list1;
}
}
This is becoming quite large but I will continue there is an issue with above implementation if hashedFile is a class not a struct (and sometimes when a stuct maybe version depdendant). Except uses an internal Set class the relevant part of that which is problematic is that it compares the hash codes and only if they are equal does it then use the comparer to check equality.
int hashCode = this.InternalGetHashCode(value);
for (int i = this.buckets[hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[i].next)
{
if ((this.slots[i].hashCode == hashCode) && this.comparer.Equals(this.slots[i].value, value))
{
return true;
}
}
The fix for this depending on performance requirements is you can just return a 0 hash code. This means the comparer will always be used.
public override int GetHashCode()
{
return 0;
}
The other option is to generate a proper hash code this matters sooner than I expected the difference for 500 items is 7ms vs 1ms and for 5000 items is 650ms vs 13ms. So probably best to go with a proper hash code. byte array hash code function taken from https://stackoverflow.com/a/7244316/1002621
public override int GetHashCode()
{
var hashCode = 0;
var bytes = _fileHash.Union(Encoding.UTF8.GetBytes(_fileString)).ToArray();
for (var i = 0; i < bytes.Length; i++)
hashCode = (hashCode << 3) | (hashCode >> (29)) ^ bytes[i]; // Rotate by 3 bits and XOR the new value.
return hashCode;
}

C# syntax if (value=this || this|| this) do this

just to throw some code out there
string answer = "hotel"
if (answer == "house"|| answer == "hotel" || answer =="appartment")
{
DoSomething()
}
I was wondering if there was some way to shorten it to
string answer = "hotel"
if (answer == "house"|| "hotel" || "appartment")
{
DoSomething()
}
I am aware of the switch statement
switch (answer)
{
case "house":
case "hotel":
case "appartment": DoSomething();
break;
default :DoNothing();
}
I am just wondering if there is some syntax sugar like what I described above.
It is possible to use some syntactic sugar for this:
if((new[]{"house", "hotel", "apartment"}).Contains(answer))
{
}
Note that this will create a new array on the fly, so will potentially be more expensive than just the Boolean checks.
You could use an array and use Contains.
So in your example:
string answer = "hotel";
string[] acceptable = new string[]{"house", "hotel", "appartment"};
if (acceptable.Contains(answer)){
DoSomething();
}
public static bool Any<T>(T item, params T[] items)
{
return items.Contains(item);
}
Usage:
if (Any(6, 1, 2, 3, 4, 5, 6, 7))
{
// 6 == 6
}
if (Any("Hi", "a", "cad", "asdf", "hi"))
{
}
else
{
// "Hi" != "hi" and from anything else.
}
Or:
public string[] items = new string[] {"a", "cad", "asdf", "hi"};
...
if (Any("Hi", items))
{
Works just as well.
}
You can also have more advanced comparison. For example, if you wanted:
if (person.Name == p1.Name ||
person.Name == p2.Name ||
person.Name == p3.Name ||
person.Name == p4.Name ||
person.Name == p5.Name || ...)
{
}
You can have:
public static bool Any<T>(T item, Func<T, T, bool> equalityChecker, params T[] items)
{
return items.Any(x => equalityChecker(item, x));
}
And do:
if (Any(person, (per1, per2) => p1.Name == p2.Name, p1, p2, p3, p4, p5, ...)
{
}
EDIT
If you insist, you can make it, of course, an extension method:
public static bool Any<T>(this T item, params T[] items)
{
return items.Contains(item);
}
Usage:
var b = 6.Any(4, 5, 6, 7); // true
And the same logic of adding the keyword "item" in the signature goes for the overload with the equalityChecker.
Create a Global List and just check it in any method.
List<string> validAnswers = new List<string> {"house", "house1", "apartment"};
if (validAnswers.Contains(answer))
DoSomething();
In this case your List will not be generated every time answer is checked
Only thing I can think of is to work with a list or array:
List<String> words = new List<String> { "house", "hotel", "appartment" };
String answer = "house";
if (words.Contains(answer))
{
DoSomething();
}
You can do like
if(new string[]{"house","hotel","appartment"}.Contains(asnwer))
{
...
}
or
if(new List<string>(){"house","hotel","appartment"}.Any(x=>x == answer)
{
}
Can add this like an extension method too, and use...
Consider adding an extension method that will accept any strings...
string answer = "hotel"
if (answer.EqualsAny("house", "hotel", "appartment"))
{
DoSomething()
}
// Extending the thought to another version
if (answer.EqualsAll("house", "hotel", "appartment"))
{
DoSomething()
}
public static class Extensions
{
public static bool EqualsAny(this string value, params string[] compareAgainst)
{
foreach (var element in compareAgainst)
{
if(value == element)
return true;
}
return false;
}
public static bool EqualsAll(this string value, params string[] compareAgainst)
{
foreach (var element in compareAgainst)
{
if(value != element)
return false;
}
return true;
}
}
If your comparison parameters are fixed, you can opt for ENUM with Flags. Ref MSDN.
Then you can have the desired behavior.
You can also refer to this post
I normally use a switch statement in this case, but as already said in a few answers above you can put the results in an list or array and check it that way.
I would use an extension method like this:
public static bool In<T>(this T item, params T[] items)
{
return items.Contains(item);
}
Use it like this:
if ("hotel".In("house", "hotel", "apartment"))
or
if (1.In(1,2))

Categories