I'm trying to write a dynamic sort of command line processor where I have a dictionary with keys being possible parameters, and the member being an Action where the string is the text between the parameters passed on the command line. Want to be able to add parameters just by adding the params array, and writing the action in the dictionary.
Yes I realize this is a pointless exercise in overcomplicating implementation to simplify maintenance. Mostly just trying to stress myself to learn more linq.
Here's my dictionary:
private static Dictionary<string[], Action<string>> _commandLineParametersProcessor = new Dictionary<string[], Action<string>>()
{
{
new string[] {"-l", "--l", "-log", "--log"},
(logFile) =>
{
_blaBla.LogFilePath = logFile;
}
},
{
new string[] { "-s", "--s", "-server", "--server" },
(server) =>
{
ExecuteSomething(server);
_blaBla.Server = server;
}
}
};
What's the most elegant mechanism to take string[] args and not just correlate the members that fall within any of the dictionary key arrays, but Aggregate((x,y) => string.Format("{0} {1}", x, y)) the sequence of elements (was thinking TakeWhile() fits in here somehow) inbetween the args[] members that would be Contain()ed in any of the keys arrays, and handing them into the action of the respective key's value member.
We have all written these little command line processors countless times, and while obviously a simple loop and switch is always more than adequate, this is again as I said an exercise trying to stress my linq skills. So please no complaints that I'm overengineering, that part is obvious.
Update:
To make this maybe a little easier, here is a non-linq way of doing what I'm looking for (may be imperfect, this is just winging it):
Action<string> currentAction;
string currentActionParameter;
for(int i = 0; i < e.Args.Length; i++)
{
bool isParameterSwitch = _commandLineParametersProcessor.Keys.Any((parameterChoices) => parameterChoices.Contains(e.Args[i]));
if (isParameterSwitch)
{
if (!string.IsNullOrEmpty(currentActionParameter) && currentAction != null)
{
currentAction(currentActionParameter);
currentAction = null;
currentActionParameter = "";
}
currentAction = _commandLineParametersProcessor[_commandLineParametersProcessor.Keys.Single((parameterChoices) => parameterChoices.Contains(e.Args[i]))];
}
else
{
currentActionParameter = string.Format("{0} {1}", currentActionParameter, e.Args[i]);
}
}
This is not an altogether bad approach, I just wonder if anyone can maybe simplify it a little using linq or otherwise, though this may be the simplest form i guess..
Borrowing half of Adam Robinson's answer (+1 btw), but realizing that the Dictionary will never be accessed by key, and you just want to run the Actions instead of building up a string...
var inputCommands = args
.Select((value, idx) => new { Value = value, Group = idx / 2 })
.GroupBy(x => x.Group)
.Select(g => new
{
Command = g.First().Value,
Argument = g.Last().Value
}).ToList();
inputCommands.ForEach(x =>
{
Action<string> theAction =
(
from kvp in commands
where kvp.Key.Contains(x.Command)
select kvp.Value
).FirstOrDefault();
if (theAction != null)
{
theAction(x.Argument);
}
}
kvp.Key.Contains really defeats the whole point of Dictionary. I'd re-design that to be a Dictionary<string, Action<string>>. Then you could say
inputCommands.ForEach(x =>
{
if (commands.ContainsKey(x.Command))
{
commands[x.Command](x.Argument);
}
}
PS: I can recall much more obtuse C# code that I have written than this.
I must admit the possibility that you want to collect the actions, instead of running them. Here is that code:
var todo =
(
from x in inputCommands
let theAction =
(
from kvp in commands
where kvp.Key.Contains(x.Command)
select kvp.Value
).FirstOrDefault()
where theAction != null
select new { TheAction = theAction, Argument = x.Argument }
).ToList();
Assuming you know that every command has a corresponding argument (so 'args' will always be in the format of
cmd arg (repeated)
You could do something ridiculous like this...
var output = args.Select((value, idx) => new { Value = value, Group = idx / 2 })
.GroupBy(x => x.Group)
.Select(g => new
{
Command = commands.FirstOrDefault(kvp =>
kvp.Key.Contains(g.First().Value)).Value,
Argument = g.Last().Value
})
.Where(c => c.Command != null)
.Aggregate(
new StringBuilder(),
(builder, value) =>
{
builder.AppendLine(value.Command(value.Argument));
return builder;
}).ToString();
But that is, frankly, the most obtuse bit of C# that I can recall ever writing, and not a very good way to teach yourself LINQ. Nonetheless, it will do what you're asking.
EDIT
Just realized (thanks to David B) that your key is a string[], not just a string, so I added some even more obtuse code that deals with that.
Related
I have a code :
{
int i = 0;
Guid[] ids = new Guid[clientCertifications.Count()];
foreach (Certification certif in clientCertifications)
{
ids[i] = certif.Id;
i++;
}
return listOffices.GroupBy(lo => lo.pk_Office)
.Select(loG => loG.First()
.MapOfficeToModel(
loG.Where(g => g.FK_Guid.In(ids)).Select(g => g.FK_Guid).ToCertifications(clientCertifications)
));
}
I would like to know if it is possible to obtain the list "ids" using a select or other word of linq? In the example I use a loop for and foreach, but I think we can do shorter no? In the line :
loG.Where(g => g.FK_Guid.In(***here something like: clientCertifications.Select(o => o.Id ... )*** ids)).Select(g => g.FK_Guid).ToCertifications(clientCertifications)`
This piece of your code:
int i = 0;
Guid[] ids = new Guid[clientCertifications.Count()];
foreach (Certification certif in clientCertifications)
{
ids[i] = certif.Id;
i++;
}
is basically the complicated version of:
var ids = clientCertifications.Select(certif => certif.Id).ToArray();
And you should be able to put clientCertifications.Select(certif => certif.Id).ToArray() wherever you would have used the variable ids if it's plain LinQ. If you have a provider for LinQ that does transformations (for example to database statements) that may not work and you may need the temporary variable. But then, if you do use such a provider, there might be an entirely different and maybe better way.
I have a list of transactions and i need to find if there is more then 1 account
i did
var MultipleAccounts = list.GroupBy(t => t.AccountId).Count() > 1;
is there a better way?
If you're willing to lose the single-line I prefer the use of !.All(item => bool) or .Any(item => bool) as I think it's the most semantic and easiest to read, as well as being a good candidate for the fastest.
var accountId = accounts[0].AccountId;
var hasMultipleAccounts = !accounts.All(account => account.AccountId == accountId);
Alternatively, and perhaps even more semantically, you could use .Any(item => bool) instead of .All(item => bool).
var accountId = accounts[0].AccountId;
var hasMultipleAccounts = accounts.Any(account => account.AccountId != accountId);
Things to watch out for are making sure you have at least one item (so that accounts[0] doesn't fail) and not doing a multiple enumeration of your IEnumerable. You say you're working with a List, so multiple enumeration shouldn't cause you any trouble, but when you just have an unknown IEnumerable it's important to be careful.
I prefer:
var MultipleAccounts = list.Select(t => t.AccountId).Distinct().Skip(1).Any();
This should be exceedingly fast as it will stop iterating the source list as soon as it finds a second AccountId.
Anytime you execute a full .Count() it has to iterate the full source list.
You can test this with the following code:
void Main()
{
Console.WriteLine(Data().Select(t => t).Distinct().Skip(1).Any());
}
private Random __random = new Random();
public IEnumerable<int> Data()
{
while (true)
{
var #return = __random.Next(0, 10);
Console.WriteLine(#return);
yield return #return;
}
}
A typical run looks like this:
7
9
True
Ok here is what i found the quickest
public bool HasMultipleAccounts(List<Account> list)
{
foreach (var account in list)
if (account.AccountId != list[0].AccountId)
return true;
return false;
}
usage: var MultipleAccounts = HasMultipleAccounts(list);
Credits: #hvd
i know its more code but if you think what the cpu needs to do its the quickest
I'm having a string like "a.b.c.d.e".
If I want to get an array like "a.b.c.d.e", "b.c.d.e", "c.d.e", "d.e", "e" in C#. What's the simplest approach?
Something like this will do:
var stringParts = input.Split('.');
var result = Enumerable.Range(0, stringParts.Length)
.Select(i => string.Join(".", stringParts.Skip(i)));
But like I said in my comment, please show the code you came up with and why you want to make it a one-liner, which usually doesn't serve any benefit. This isn't codegolf.
If you really do it with one statement, you can try this:
var str = "a.b.c.d.e";
var parts = str.Split('.')
.Select((x,idx) => new { idx })
.Select(p => string.Join(".",
str.Split('.').Skip(p.idx))).ToList();
This could be more efficient if you use Split first:
var parts = str.Split('.');
var result = parts
.Select((x,idx) => new { idx })
.Select(p => string.Join(".",
parts.Skip(p.idx))).ToList();
You can also do it without creating anonymous type(s), just create an int variable:
int i = 0;
var result = parts
.Select(p => string.Join(".", parts.Skip(i++)))
.ToList();
This is fairly neat:
var text = "a.b.c.d.e";
var results =
text
.Split('.')
.Reverse()
.Scan("", (a, x) => x + "." + a)
.Select(x => x.TrimEnd('.'))
.Reverse();
You do need to add the Microsoft Reactive Extensions Team's "Interactive Extensions" to get the Scan operator. Use NuGet and look for "Ix-Main".
I actually kind of like this question, not necessarily production but a bit of brain-bendy fun:
"a.b.c.d.e".Split('.').Reverse()
.Aggregate(Enumerable.Empty<string>(), (acc, c) =>
acc.Concat(new [] { c+(acc.LastOrDefault()??"") })
).Reverse()
Dotnetfiddle
What this does is move through each character in the split array and build up a new array by prepending the last value in the array with the current character. It's a fairly common functional programming technique.
Well, this is how I might write it.. I know, not "one line", but if you're gonna use (and I do recommend) a method anyway..
IEnumerable<string> AllComponentPartsForward (string s) {
IEnumerable<string> p = s.Split('.');
while (p.Any()) {
yield return string.Join(".", p); // p.ToArray() for .NET 3.5
p = p.Skip(1);
}
}
(I suppose it could be "more efficient" with IndexOf/Substring, but that's also harder for me to write and reason about!)
I have a snippet as follow. Basically I would like to create a dictionary from the series I currently have. If I did it the long way, there was no problem, the code run fine and I got the expected results.
class Program
{
static void Main(string[] args)
{
List<int> series = new List<int>() { 1,2,3,4 };
Dictionary<int, List<int>> D = new Dictionary<int,List<int>>();
foreach (var item in series)
{
D.Add(item, seriesWithinSeries(item));
}
}
public static List<int> seriesWithinSeries(int seed)
{
return Enumerable.Range(0, seed).Where(x => x < seed).ToList();
}
}
How do I convert that into Linq? I have tried this:
D = series.Select(x=> { return new (x,seriesWithinSeries(x));}).ToDictionary<int,List<int>>();
But the compiler complaints I need a type for the new, which makes sense. However, I do not really know how to fix it. Please help.
ToDictionary doesn't have a parameterless version. You probably want this:
var result = series.ToDictionary(x => x, x => seriesWithingSeries(x));
So anonymous type is not needed here. But for the sake of a complete explanation here is a correct syntax with an anonymous type:
var result = series
.Select(x => new { Key = x, Value = seriesWithinSeries(x) })
.ToDictionary(pair => pair.Key, pair => pair.Value);
In C# i have a List which contains numbers in string format. Which is the best way to count all this numbers? For example to say i have three time the number ten..
I mean in unix awk you can say something like
tempArray["5"] +=1
it is similar to a KeyValuePair but it is readonly.
Any fast and smart way?
Very easy with LINQ :
var occurrenciesByNumber = list.GroupBy(x => x)
.ToDictionary(x => x.Key, x.Count());
Of course, being your numbers represented as strings, this code does distinguish for instance between "001" and "1" even if conceptually are the same number.
To count numbers that have the same value, you could do for example:
var occurrenciesByNumber = list.GroupBy(x => int.Parse(x))
.ToDictionary(x => x.Key, x.Count());
(As noted in digEmAll's answer, I'm assuming you don't really care that they're numbers - everything here assumes that you wanted to treat them as strings.)
The simplest way to do this is to use LINQ:
var dictionary = values.GroupBy(x => x)
.ToDictionary(group => group.Key, group => group.Count());
You could build the dictionary yourself, like this:
var map = new Dictionary<string, int>();
foreach (string number in list)
{
int count;
// You'd normally want to check the return value, but in this case you
// don't care.
map.TryGetValue(number, out count);
map[number] = count + 1;
}
... but I prefer the conciseness of the LINQ approach :) It will be a bit less efficient, mind you - if that's a problem, I'd personally probably create a generic "counting" extension method:
public static Dictionary<T, int> GroupCount<T>(this IEnumerable<T> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
var map = new Dictionary<T, int>();
foreach (T value in source)
{
int count;
map.TryGetValue(number, out count);
map[number] = count + 1;
}
return map;
}
(You might want another overload accepting an IEqualityComparer<T>.) Having written this once, you can reuse it any time you need to get the counts for items:
var counts = list.GroupCount();