i have this class
public class ConnectionResult
{
private int connectionPercentage;
public int ConnectPercentage
{
get { return connectionPercentage; }
}
public ConnectionResult(int ip)
{
// Check connection and set connectionPercentage
}
}
and i have a manager that gets several lists of ConnectionResult and count each value greater then a specific number determined by configuration. my implementation is so:
public class CurrentConnections
{
private static CurrentConnections inst;
private CurrentConnections()
{
}
public static CurrentConnections GetInstance
{
get
{
if (inst != null)
{
inst = new CurrentConnections();
}
return inst;
}
}
public int CountActiveConnections(params List<ConnectionResult>[] conns)
{
int rtVal = 0;
foreach (List<ConnectionResult> connectionResult in conns)
{
foreach (var currConn in connectionResult)
{
if (currConn.ConnectPercentage > ACCEPTABLE_CONNECTION)
{
rtVal++;
}
}
}
return rtVal;
}
}
but i want to make it better, so i started to write it in linq and i got to
conns.Count(x => x.Count(y => y.ConnectPercentage > ACCEPTABLE_CONNECTION));
but this gives me an error of Cannot implicitly convert type 'int' to 'bool'.
is there a way to count it in linq or do i have to stay with what i wrote?
btw, i'm new to linq
You're using Count twice, and I don't think you want to. I think you just want:
return conns.SelectMany(list => list)
.Count(conn => conn.ConnectPercentage > ACCEPTABLE_CONNECTION);
The SelectMany call is to flatten the "array of lists" into a single sequence of connections.
John Skeet's answer is excellent, but to address the error that you're seeing, the query would be:
conns.Sum(x => x.Count(y => y.ConnectPercentage > ACCEPTABLE_CONNECTION));
Count accepts a function which returns bool and returns the number of items from the collection which meet that criteria.
Sum accepts a function which returns int (among others), and returns the sum of the results of the expression applied to each item.
Of course, whether you select every item from each subset and then count them up (like John Skeet suggests), or you count the items from each subset and then add up the counts (like my code suggests), the result will be exactly the same.
return conns.SelectMany(x=> x).Where(conn => conn.ConnectPercentage > ACCEPTABLE_CONNECTION).;
Related
I have two classes like this
public class Stock
{
public StockItem Item;
public string Location;
public int Quantity;
public string Price;
}
public class StockItem
{
public string Code;
public string Name;
public string Unit;
}
And I have a list that contains multiple instances of the Stock class
var stockList = new List<Stock>();
I am trying to determine whether the Name property of each instance inside the list is equal to a predefined string. Currently, I am looping through the list like this
foreach (var stock in stockList)
{
if (stock.Item.Name.ToLower() == "test")
{
Console.WriteLine("Found the class")
break;
}
}
However, I was wondering if there was a more efficient way to do this using Linq or something similar to the .Contains method. Is there a simpler or more efficient way to accomplish this?
whether the Name property of each instance inside the list is equal
to a predefined string
Not much more efficient but simpler:
bool allAreEqual = stockList.All(x => x.Item.Name.Equals("test", StringComparison.OrdinalIgnoreCase))
If you instead want to find the first which matches the condition(what your loop really does):
Stock si = stockList.FirstOrDefault(x => x.Item.Name.Equals("test", StringComparison.OrdinalIgnoreCase));
Now you know if such a Stock exists(si != null) at all and you got it.
All in linq will return True or false
stockList.All(p => p.Item.Name.ToLower() == "test");
You can use the Linq Any() method:
bool containsItemName = stockList.Any(x => x.Item.Name.Equals("MyName", StringComparison.InvariantCultureIgnoreCase));
Are you really looking at all instances? From your question, it seems as if Anymight be the way to go, see here.
stockList.Any(p => p.Item.Name.ToLower() == "test");
You can get a result what you wanted by calling Any
bool result = stockList.Any(
stock => stock.Item.Name.Equals("text", StringComparison.InvariantCultureIgnoreCase)
);
In this code, the parameter name stock can be changed whatever you want.
The Scope of this Project is just a console application and I get a pretty annoying
The error I get when trying to pass the value:
Argument type 'lambda expression' is not assignable to parameter type 'DungeonRunner.Ability'
The function where I want to pass the parameter:
public void UseSpell(Ability ability)
{
var SpellToUse = this.CharClass.GetAbilities();
//Get all Abilites
this.CharClass.GetAbilities();
if (this.MP == 0)
{
Console.WriteLine("You cannot use advanced Abilities, drink a Mana Potion to restore Mana");
var UsedSpell = SpellToUse.First();
this.MP -= UsedSpell.ManaCost1; //This line is unnecessary because the first spell costs nothing
}
else if(this.MP >= 0)
{
var UsedSpell = SpellToUse.Find(abilityName => abilityName.SpellName1 == ability.SpellName1);
this.MP -= UsedSpell.ManaCost1;
}
The class I reference to:
namespace DungeonRunner
{
public class Ability
{
private int ManaCost;
private int SpellDamage;
private string SpellName;
public Ability(int manaCost, int spellDamage, string spellName)
{
ManaCost = manaCost;
SpellDamage = spellDamage;
SpellName = spellName;
}
public int ManaCost1
{
get => ManaCost;
set => ManaCost = value;
}
public int SpellDamage1
{
get => SpellDamage;
set => SpellDamage = value;
}
public string SpellName1
{
get => SpellName;
set => SpellName = value;
}
}
}
This is the value I try to pass:
`MyCharacter.UseSpell(ability => ability.SpellName == spellname)`;
The question is: How can I possibly optimize this so that the error goes away.
It may be that I'll need to change the paramter. But I dont think it's needed.
Assuming the list with all abilities is called abilities you can write the following code;
Ability usedAbility = abilities.FirstOrDefault(x => x.SpellName1 == spellname);
if(usedAbility != null)
{
MyCharacter.UseSpell(usedAbility);
}
With FirstOrDefault you're getting the first Ability with the spellname. I added the null check because if there is no Ability with that name you will get null from FirstOrDefault.
There are actually two ways you can do.
The first is provide the actual Ability to your method and let it do something with that instance:
public void UseSpell(Ability ability)
{
// do something with the provided ability
}
This way your client has to search for the correct ability. To do so it has to know all abilities:
UseSpell(myAbilities.FirstOrDefault(x => x.Spellname == "MySpell"));
The other opportunity is that UseSpell searches for an Ability itself based on some condition, which you can provide by a delegate. However that assumes that UseSpell has access to all abilities in order to search for the "right" one:
public void UseSpell(Func<Ability, bool> predicate)
{
// get all abilities
var allAbilties = ...;
// get the one that matches the condition
var ability = allAbilities.FirstOrDefault(predicate)
}
Now the client only provides the criterion, but not the entire list of abilities:
UseSpell(x => x.Spellname == "MySpell");
So depending on where your list of all abilites is located, either the one or the other approach might be better. The result is the same in both cases. However the semantics are different.
Reference is wrong. Do the following
MyCharacter.Where(ability => ability.SpellName == spellname).Foreach(ability => UseSpell(ability));
I'm trying, unsuccessfully, to enumerate through a List<Func<InstagramUser,bool>> to filter a collection of List<InstagramUser>. My code compiles, but just returns the whole list, unfiltered, when display() is invoked.
My question is, what is the proper way to hold a collection of lambda expressions that can then be used to filter another collection?
public class InstagramDisplay {
public IList<InstagramUser> instagramUsers;
public IList<Func<InstagramUser, bool>> instagramFilters;
public InstagramDisplay() {
instagramUsers = new List<InstagramUser>();
instagramFilters = new List<Func<InstagramUser, bool>>();
}
public void addFilter(Func<InstagramUser, bool> filter, object filterValue) {
if ((int)(filterValue ?? 0) > 0)
instagramFilters.Add(filter);
}
public IEnumerable<InstagramUser> display() { //filter not working
instagramFilters.ToList().ForEach(filter => instagramUsers.Where(filter));
return instagramUsers;
}
}
Invocation - just returns full collection, unfiltered:
InstagramDisplay instagramDisplay = new InstagramDisplay();
instagramDisplay.instagramUsers = (List<InstagramUser>)context.Cache[CACHE_KEY];
instagramDisplay.addFilter(u => u.id == instagramId, instagramId);
context.Response.Write(javascriptSerializer.Serialize(instagramDisplay.display());
If you want to include users that matches all the filters (an AND comparison):
instagramUsers.Where(u => instagramFilters.All(f => f(u)));
If you want to include users that matches 1 or more filters, change the All by Any in the statement above.
The problem is that your ForEach statement doesn't actually use or store the results of the Where method you are running. You need to build a resulting IEnumerable<T>, and filter it in series.
You could write this as:
public IEnumerable<InstagramUser> display() {
IEnumerable<InstagramUser> users = instagramUsers;
foreach(var filter in instagramFilters)
users = users.Where(filter);
return users;
}
private class CompAdvertisements : IComparer<Advertisements>
{
private string OrderBy { get; set; }
public CompAdvertisements(string orderBy)
{
OrderBy = orderBy;
}
#region IComparer<Advertisements> Members
public int Compare(Advertisements x, Advertisements y)
{
return x.Country.Name.CompareTo(y.Country.Name);
Can i also user x.Name.CompareTo(y.Name); in comparer that i will compare with two elements lik order by something and order by something2
Yes. If the outer comparison indicates that your two elements (Country.Name) are the same, then you instead return the result of an inner comparison (somethingElse). You can do that for an arbitrary depth of comparisons.
outerCompare = x.Country.Name.CompareTo(y.Country.Name);
if (outerCompare != 0)
{
return outerCompare;
}
else
{
return (x.Name.CompareTo(y.Name));
}
Eric J. is right. You may also want to have a look at this stackoverflow question. The answers there give several ways you can sort a List, and they also go into detail about using an IComparer object to perform a similar task to what you're doing.
Good Morning!
Given:
public class FooClass
{
public void FooMethod()
{
using (var myEntity = new MyEntity)
{
var result = myEntity.MyDomainEntity.Where(myDomainEntity => myDomainEntity.MySpecialID > default(int)).Distinct(new FooComparer);
}
}
}
public class FooComparer : IEqualityComparer<MyEntity.MyDomainEntity>
{
public bool Equals(MyEntity.MyDomainEntity x, MyEntity.MyDomainEntity y)
{
return x.MySpecialID == y.MySpecialID;
}
public int GetHashCode(MyEntity.MyDomainEntity obj)
{
return obj.MySpecialID.GetHashCode();
}
}
This will compile, but on runtime I will get an Linq to Entity could not translate Comparer-Exception.
Any suggestions?
If you're providing your own comparisons, you'll need to execute the Distinct call in .NET code. To make sure that happens, use AsEnumerable to turn IQueryable<T> into IEnumerable<T>:
var result = myEntity.MyDomainEntity
.Where(myDomainEntity => myDomainEntity.MySpecialID > default(int))
.AsEnumerable()
.Distinct(new FooComparer());
Of course at that point you'll be pulling more data across from the database. An alternative is to group the data instead:
var result = from entity in myEntity.MyDomainEntity
where entity.MySpecialID > 0
group entity by entity.MySpecialID into groups
select groups.FirstOrDefault();
That will get you the first entity encountered with each ID (assuming my query-fu isn't failing me). That's basically what Distinct does anyway, but it's all at the database.
(Note to future readers: calling First() makes more sense than FirstOrDefault(), but apparently that doesn't work.)