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
Related
if (gardenvlist.Count() == days)
{
var g = gardenvlist;
}
if (oceanvlist.Count() == days)
{
var o = oceanvlist;
}
if (cityvlist.Count() == days)
{
var c = cityvlist;
}
var final = g.Union(o).Union(c);
if (final.Count() > 0)
{
return new ObjectResult(final);
}
return NotFound();
So, what I have over here is I wanted to check if the gardenvlist are available within the period. If the list is available in the period, select the list. After that check the oceanvlist and so on. Next, it will check that if the final result contains any lists. If there's one or many lists, return those lists else return false.
Sorry if my explanation is not clear enough. I'm new to programming.
You do not need the Select in each if condition because you do not do anything with the projection (select). Also, declare your final variable at the beginning and add to it inside the if blocks.
var rateGroupIds = new List<int>();
if (gardenvlist.Count() == days)
{
rateGroupIds.AddRange(gardenvlist.Select(x => RateGroupID));
}
if (oceanvlist.Count() == days)
{
rateGroupIds.AddRange(oceanvlist.Select(x => RateGroupID));
}
if (cityvlist.Count() == days)
{
rateGroupIds.AddRange(cityvlist.Select(x => RateGroupID));
}
Try something like this:
var final = new List<T>();
// Add all the lists only if they match
UnionIfMatches(gardenvlist);
UnionIfMatches(oceanvlist);
UnionIfMatches(cityvlist);
if (final.Count() > 0)
return new ObjectResult(final);
return NotFound();
// ---- Local Functions ---- //
// Adds the list to final if it matches
void UnionIfMatches(List<T> list)
{
if (ListMatches(list))
final.Union(list);
}
// Checks if the list matches
bool ListMatches(List<T list> => list.Select(x => xmethod.RateGroupID.Count() == days);
You used the same code 3 times, so I just moved the testing if it matches into a new function ListMatches() to make it easier. I then made a second function that will add in that list only if it matches.
I don't know what type you were using, because you used var, so I'm just guessing it was a List<T>. If it wasn't, just swap it out with the real class and it should still work.
These local functions are only usable and visible from within the scope of the function, which is really useful.
I couldn't test this, so let me know how this works.
I'm using the RemoveAll() statement, that do a foreach element of the list, and based in a condition returned by the delegate, it removes or not the element from the list. Like this:
x.RemoveAll(delegate(string y)
{
if (y == "abc")
return true;
return false;
});
I want to break the foreach from the removeAll, so that upon fulfilling some condition, I no longer even try to remove elements. Something like this:
x.RemoveAll(delegate(string y)
{
if (Foo() || Bar())
break; //stop trying to remove elements
if (y == "abc")
return true;
return false;
});
Have a way to do this without a auxiliary variable?
P.S: Using a auxiliary variable I know how to do.
There are two real options. A variable telling you not to remove more items:
var done = false;
list.RemoveAll(item =>
{
if(done) return false;
if(Foo() || Bar())
{
done = true;
return false;
}
return item == "abc";
}
Or throwing an exception (despite the fact that it's really poor practice to use exceptions for control flow).
list.RemoveAll(item =>
{
if(Foo() || Bar())
throw new SomeTypeOfException()
return item == "abc";
}
If Foo or Bar being true really are exceptional/error cases, then maybe you could justify it, but it certainly seems like code smell. Note that this is technically going to be the only way to use RemoveAll and not actually invoke the delegate on any later items.
Fundamentally the problem is that the operation you're trying to perform isn't in line with what RemoveAll was designed to do. What you really want is a version of the method that supports cancellation, or sufficient access to the internals of the list to create a comparable method with the appropriate cancellation. Sadly, you don't have access to the underlying array in order to be able to replicate the ability of RemoveAll to remove multiple items without moving up all of the items until the very end, unless you re-create your own entire list based structure.
Using simple loop with break will be way more efficient then using DeleteAll with trigger to generate false for all elements after a certain point.
Why not rather filter first:
foreach(var item in x.Where(o => Foo(o)).ToList())
x.Remove(item);
If you care about efficiency, then
for(int i = 0; i++; i < x.Length)
if(Foo(x[i]))
{
x.RemoveAt(i);
break; // if list is sorted
}
else
i++;
For unsorted list it's more optimal to go from top to bottom afaik.
You could do this with an extension method:
public static IEnumerable<T> RemoveAllUntil<T>(
this IEnumerable<T> input,
Predicate<T> match,
Predicate<T> until)
{
bool untilFound = false;
foreach (T element in input)
{
if(!untilFound) untilFound = until(element);
if(untilFound || !match(element))
{
yield return element;
}
}
}
And use it like this:
var strings = new List<string> { "s1", "s2", "s2", "break", "s2", "s3"};
strings = strings.RemoveAllUntil(
s => s == "s2",
s => s == "break")
.ToList();
This will give you:
s1, break, s2, s3
Long comment: approximate code for removing item with cancellation and avoiding multiple copying of the tail:
void RemoveWithCancelation(this List<T> list,
Func<RemoveWithCancelationResult> predicate)
{
var indexToKeep = -1;
for (var i = 0; i < list.Count; i++)
{
var condition = predicate(list[i]);
if (condition.Cancel)
break;
if (!condition.RemoveItem)
{
indexToKeep++;
list[indexToKeep] = list[i];
}
}
if (indexToKeep+1 < list.Count)
list.RemoveRange(indexToKeep+1, list.Count);
}
I don't think this can be done in the RemoveAll().
However, you could 'simulate' the break with TakeWhile() and then filter the list with Where()
var newList = x.TakeWhile(elem => elem != "your condition").Where(elem => elem == "abc");
You could take advantage of closures:
bool stop = false; // the delegate closes over 'stop'
x.RemoveAll(delegate(string y)
{
if (!stop && y == "abc")
return true;
if (Foo() || Bar())
stop = true;
return false;
});
I have a list List<string> with some paths.
C:\Dir\Test\
C:\MyDir\
C:\YourDir\
C:\Dir\
I want to go through all the elements (using LINQ) and remove entries that are started with other element from my list.
In my example C:\Dir\Test\ starts with C:\Dir\ - so I want to remove C:\Dir\Test\.
Use List<T>.RemoveAll() method:
sourceList.RemoveAll(x => sourceList.Any(y => x != y && x.StartsWith(y)));
Try this:
myInitialList.RemoveAll(x =>myInitialList.Any(q => q != x && q.StartsWith(x)));
Or if you want to keep the original list, this is a way to get all the records that do not match your criteria:
List<string> resultList = myInitialList.Except(x => myInitialList.Any(q => q != x && q.StartsWith(x)));
How about
mylist = mylist.Where(a => mylist.All(b => b == a || !a.StartsWith(b)))
.Distinct()
.ToList();
This will return a new list where there isn't another item in the list that it starts with.
It has the extra check to allow returning the value where there string is the same, otherwise all items would be removed from the list.
Finally the distinct call means that two occurrences of the same string are removed.
Building on nsinreal's comment and solution you could do something like
myList = myList.OrderBy(d => d)
.Aggregate(new List<string>(),
(list, item) => {
if (!list.Any(x => item.StartsWith(x)))
list.Add(item);
return list;
}).ToList();
This reduces the complexity of the solution by reducing the size of the search list for each test. It still requires an initial sort.
Personally I find this alternative solution harder to read and my first answer is more expressive the problem to solve.
The most efficient way is IMO to sort the paths, then iterate them and return only the ones not starting as one of the previous, i.e. :
public static IEnumerable<string>
GetRootPathsOfSet(this IEnumerable<string> paths)
{
var sortedSet = new SortedSet<string>(paths,
StringComparer.CurrentCultureIgnoreCase);
string currRoot = null;
foreach (var p in sortedSet)
{
if (currRoot == null ||
!p.StartsWith(currRoot, StringComparison.InvariantCultureIgnoreCase))
{
currRoot = p;
yield return currRoot;
}
}
}
Some notes:
All the paths MUST terminate with a trailing back-slash, otherwise the StartsWith approach is not safe (e.g. C:\Dir and C:\Directory)
This code uses case-insensitive comparison
I'm not using pure LINQ here, but it's an extension method
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.
The following search method works fine for up to two terms.
How can I make it dynamic so that it is able to handle any number of search terms?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestContains82343
{
class Program
{
static void Main(string[] args)
{
List<string> tasks = new List<string>();
tasks.Add("Add contract to Customer.");
tasks.Add("New contract for customer.");
tasks.Add("Create new contract.");
tasks.Add("Go through the old contracts.");
tasks.Add("Attach files to customers.");
var filteredTasks = SearchListWithSearchPhrase(tasks, "contract customer");
filteredTasks.ForEach(t => Console.WriteLine(t));
Console.ReadLine();
}
public static List<string> SearchListWithSearchPhrase(List<string> tasks, string searchPhrase)
{
string[] parts = searchPhrase.Split(new char[] { ' ' });
List<string> searchTerms = new List<string>();
foreach (string part in parts)
{
searchTerms.Add(part.Trim());
}
switch (searchTerms.Count())
{
case 1:
return (from t in tasks
where t.ToUpper().Contains(searchTerms[0].ToUpper())
select t).ToList();
case 2:
return (from t in tasks
where t.ToUpper().Contains(searchTerms[0].ToUpper()) && t.ToUpper().Contains(searchTerms[1].ToUpper())
select t).ToList();
default:
return null;
}
}
}
}
How about replacing
switch (searchTerms.Count())
{
case 1:
return (from t in tasks
where t.ToUpper().Contains(searchTerms[0].ToUpper())
select t).ToList();
case 2:
return (from t in tasks
where t.ToUpper().Contains(searchTerms[0].ToUpper()) && t.ToUpper().Contains(searchTerms[1].ToUpper())
select t).ToList();
default:
return null;
}
By
(from t in tasks
where searchTerms.All(term => t.ToUpper().Contains(term.ToUpper()))
select t).ToList();
Just call Where repeatedly... I've changed the handling of searchTerms as well to make this slightly more LINQ-y :)
public static List<string> SearchListWithSearchPhrase
(List<string> tasks, string searchPhrase)
{
IEnumerable<string> searchTerms = searchPhrase.Split(' ')
.Select(x => x.Trim());
IEnumerable<string> query = tasks;
foreach (string term in searchTerms)
{
// See edit below
String captured = term;
query = query.Where(t => t.ToUpper().Contains(captured));
}
return query.ToList();
}
You should note that by default, ToUpper() will be culture-sensitive - there are various caveats about case-insensitive matching :( Have a look at this guidance on MSDN for more details. I'm not sure how much support there is for case-insensitive Contains though :(
EDIT: I like konamiman's answer, although it looks like it's splitting somewhat differently to your original code. All is definitely a useful LINQ operator to know about...
Here's how I would write it though:
return tasks.Where(t => searchTerms.All(term => t.ToUpper().Contains(term)))
.ToList();
(I don't generally use a query expression when it's a single operator applied to the outer query.)
EDIT: Aargh, I can't believe I fell into the captured variable issue :( You need to create a copy of the foreach loop variable as otherwise the closure will always refer to the "current" value of the variable... which will always be the last value by the time ToList is executed :(
EDIT: Note that everything so far is inefficient in terms of uppercasing each task several times. That's probably fine in reality, but you could avoid it by using something like this:
IEnumerable<string> query = tasks.Select
(t => new { Original = t, Upper = t.ToUpper });
return query.Where(task => searchTerms.All(term => task.Upper.Contains(term)))
.Select(task => task.Original)
.ToList();
Can't test code right now, but you could do something similar to this:
from t in tasks
let taskWords=t.ToUpper().Split(new char[] { ' ' });
where searchTerms.All(term => taskWords.Contains(term.ToUpper()))
select t
Replace the switch statement with a for loop :)
[TestMethod]
public void TestSearch()
{
List<string> tasks = new List<string>
{
"Add contract to Customer.",
"New contract for customer.",
"Create new contract.",
"Go through the old contracts.",
"Attach files to customers."
};
var filteredTasks = SearchListWithSearchPhrase(tasks, "contract customer new");
filteredTasks.ForEach(Console.WriteLine);
}
public static List<string> SearchListWithSearchPhrase(List<string> tasks, string searchPhrase)
{
var query = tasks.AsEnumerable();
foreach (var term in searchPhrase.Split(new[] { ' ' }))
{
string s = term.Trim();
query = query.Where(x => x.IndexOf(s, StringComparison.InvariantCultureIgnoreCase) != -1);
}
return query.ToList();
}
why not use a foreach and AddRange() after splitting the terms and saving it into a list.
List<ItemsImLookingFor> items = new List<ItemsImLookingFor>();
// search for terms
foreach(string term in searchTerms)
{
// add users to list
items.AddRange(dbOrList(
item => item.Name.ToLower().Contains(str)).ToList()
);
}
that should work for any amount of terms.