C# using list to find elements in class - c#

I'm trying to use List.Contains to find if item in list, has it's bool true or false
Problem is, no matter if the bool is true or false, function always return's false.. her's an example
public class RecepieClass
{
public Recepie recepie;
public bool isUnlocked;
}
public class RecepieList : MonoBehaviour
{
public List<RecepieClass> recepies = new List<RecepieClass>();
public void SelectRecepie(Recepie r)
{
Debug.Log(recepies.Contains(new RecepieClass{recepie = r, isUnlocked = false}));
}
}

Use a for loop, and examine the recipe property of each RecipeClass item. If you know that the Recepie parameter being passed to the function is the actual instance of one of the RecepieClass items in your list, then the default Object.Equals method will work. If it is another instance, you'll need to define your own Equals method (i.e., Recepie should implement IEquatable).
public class RecepieClass
{
public Recepie recepie;
public bool isUnlocked;
}
public class RecepieList : MonoBehaviour
{
public List<RecepieClass> recepies = new List<RecepieClass>();
public void SelectRecepie(Recepie r)
{
foreach (var rc in recepies) {
if (rc.recepie.Equals(r)) {
// you've found it
if (rc.isUnlocked) {
// do something
} else {
// do something else
}
break;
}
}
}
}
If you're into the whole brevity thing, you could use System.Linq.FirstOrDefault (just be prepared for it to return a null).
public void SelectRecepie(Recepie r)
{
if (recepies.FirstOrDefault(rc => rc.recepie.Equals(r))?.isUnlocked ?? false) {
// do something
}
}

When comparing reference types the comparison is determining if objects point to the same object (assuming class doesn't override IEquatable). What you are tying to find is an item with the same values.
The safest way to find an item within a collection is to use a unique identifier within the item combined with the FirstOrDefault() method.
Given there is nothing that seems unique on RecepieClass the example below assumes there is an Id within Recepie referenced from RecepieClass.
var recipe = recepies.FirstOrDefault(r => r.recepie?.Id == 123456);
Debug.Log(recipe);
With the above example recipe will be null or the first RecepieClass with a Recepie matching the supplied id value (123456 in the example).

I dont know if your value r is unique in List or not. i just trap the first found
you just write: (with using System.Linq)
public void SelectRecepie(Recepie r)
{
var result = recepies.FirstOrDefault(x => x.recepie.Equals(r)).isUnlocked;
Debug.Log(result);
}

Related

Matching conditional template of a unique set of objects under ambiguous conditions

It's better to start with code
public interface ICondition<TObject> where TObject : notnull
{
public TObject? Value { get; } //Condition value for primitives
public bool InverseResult { get; init; }
public bool IsSatisfiedBy(TObject obj)
{
return InverseResult ? !Check(obj) : Check(obj);
}
public bool Check(TObject obj);
}
This interface is implemented much in classes like IntegerCondition, BooleanCondition. And other primitives. For example GreaterIntegerCondition, ExactlyIntegerCondition
public interface ITemplate<TObject> : ICondition<TObject> where TObject : notnull
{
TObject? ICondition<TObject>.Value => default;
public IEnumerable<IMappedCondition<TObject>> ConditionsMap { get; }
bool ICondition<TObject>.InverseResult
{
get => false;
init { }
}
public string TemplateName { get; set; }
bool ICondition<TObject>.Check(TObject obj)
{
return ConditionsMap.All(c => c.CheckCondition(obj)) && ConditionsMap.Any();
}
}
This interface extends previous. It can contain many of conditions that mapped to TObject's properties. It is used on more complex objects that can have many properties.
At the end I have this class (simplified version below)
public class FooBunchTemplate
{
public List<ICondition<Foo>> Conditions { get; }
public bool CheckBunch(HashSet<Foo> bunch)
{
var remainingConditions = Conditions.ToList();
foreach(Foo foo in bunch)
{
var matched = remainingConditions.FirstOrDefault(c => c.IsSatisfiedBy(foo));
if(matched == null) return false;
remainingConditions.Remove(matched);
}
return true;
}
}
So, we have set of Foo. Every Foo in HashSet is unique. But problem is the one Foo must match only one condition "slot". There are templates that can be satisfied by different objects (even hundreads of them)
If I just exclude matches, then there might be a situation where the more general condition will be excluded by Foo which would fit also the narrower pattern. Then the CheckBunch() will return false, although if the conditions are set correctly, it should have returned true.
You can think of it as a slot machine. The order is unimportant, but the link 1 condition - 1 object is important.
Example template:
A or B
A
C
Bunch:
CAB
We search C template and exclue 3rd
We search A template and exclude 1st
We search B template but 1st was excluded before.
False is returned
//Wrong result. CAB matches this template, because we can put A in second slot and B in first.
Which approach will be fast and reliable in that situation. Number of conditions in BunchTemplate always fit count of Foo in bunch

Better setters for lists of objects

Recently, when handling collections of objects of the same (base-)class,
I´ve recently found myself writing something like this:
class SomeClass {
public bool PropertyA {get; set;}
}
class EncapsulatingClass {
private List<SomeClass> list = new();
private bool propA;
public bool PropertyA {
get { return propA; }
set {
propA = value;
foreach(SomeClass instance in list)
instance.PropertyA = value;
}
}
}
This is of course so I don´t have to use foreach every time I want to set a property for the collection. While this works fine, I feel like this requires a lot of code for something simple and a lot of repitition with each property.
Is there a better solution, like extracting the logic of "apply this for the property of the same name for each object in the list" into a function and just calling that in the setters?
There is the issue of ownership of the property. If you need to enforce synchronization such that setting PropertyA ins the encapsulating class, all the instances in the list also use the same value.
For example
class SomeClass
{
public SomeClass(EncapsulatingClass parent)
{
Parent=parent;
}
public EncapsulatingClass Parent { get; }
public bool PropertyA { get => Parent.PropertyA; }
}
class EncapsulatingClass
{
private List<SomeClass> list = new List<SomeClass>();
private bool propA;
public bool PropertyA
{
get { return propA; }
set
{
propA = value;
}
}
}
Otherwise, you have multiple PropertyA values, one for each instance, and then you have to decide which one is the master value, and what to do if some are different.
I'm wondering what it is you are doing to need this so often. It makes me think there's a flaw in the design of your application you could avoid by restructuring something but it's difficult to say without more information.
For your specific problem I would discard EncapsulatingClass and use the ForEach method on List<T> for a little more concise code:
myList.ForEach(s => s.PropertyA = true);
Alternatively, if you don't always use List<T> you can write your own extension method to work on all IEnumerables:
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var t in source)
action(t);
}
// Call it just like previously:
myIEnumerable.ForEach(s => s.PropertyA = true);
Of course, this is still cumbersome if you need to do it a lot. But I suspect if you do, it's probably a flaw in the design.
I might approach this with a custom List class providing a single mass update method.
public class EasyUpdateList<T> : List<T>
{
public void UpdateAll(Action<T> update)
{
if (update == null)
return;
foreach (T item in this)
update(item);
}
}
Now you don't need a specific encapsulating class, you can just create a new EasyUpdateList and update any number of properties across the collection using the UpdateAll method.
EasyUpdateList<MyClass> list = new EasyUpdateList<MyClass>();
list.Add(instance1);
list.Add(instance2);
...
list.UpdateAll(x =>
{
x.Property1 = "Value1";
x.Property2 = "Value2";
});
This still uses a foreach loop but is much more generic and you don't have to change your other classes or write repeated code for each one.
Of course you could also achieve this with an extension method for a List class if you don't want a new class.
public static void UpdateAll<T>(this IList<T> list, Action<T> update)
{
if (update == null)
return;
foreach (T item in list)
update(item);
}

Linq query List<List<class>> Where property of class C#

Could someone suggest a way for me to select one of the lists within a parent list where one of it's elements has a certain property value?
public class HierarchyLevel
{
public string Abbreviation;
public string Name;
public string Value;
public Type LevelType;
public List<HierarchyLevel> Children = new List<HierarchyLevel>();
}
public static List<List<HierarchyLevel>> ElementaryTypes = new List<List<HierarchyLevel>>();
I am actually trying to get the List that has the LevelType field of a specific type.
You wrote:
I am actually trying to get the List that has the LevelType field of a specific type.
What do you want if you've got several HierarchyLevels with this LevelType? And what do you want if there are no HierarchyLevels at all with this LevelType?
Let's assume that you want all HierarchyLevels with this LevelType. If later on you only want the first, or the one with a certain Abbreviation (or whatever), you could always use .FirstOrDefault, or .Where. and do a ToList in the end.
Implementing it as an extension function. See Extension Methods Demystified
public static IEnumerable<HierarchyLevel> GetHierarchyLevelsWithLevelType(
this IEnumerable<HierarchyLevel> hierarchyLevels,
LevelType desiredLevelType)
{
foreach (var hierarchyLevel in hierarchyLevels)
{
if (hierarchyLevel.LevelType == desiredLevelType)
{ // found one!
yield return hierarchyLevel;
}
// using recursion: check all the Children
IEnumerable<HierarchyLevel> childrenWithDesiredLevelType = hierarchyLevel.Children
.GetHierarchyLevelsWithLevelType(desiredLevelType);
foreach(var childWithDesiredLevelType in childrenWithDesiredLevelType)
{
yield return childWithDesiredLevelType;
}
}
}
Because of the recursion all Grandchildren and their Children etc will be returned
usage:
var allSpecialHierarchies = myHierarchies.GetHierarchyLevelsWithLevelType(LevelType.Special);
// get the first:
var firstSpecialHierarchy = allSpecialHierarchies.FirstOrDefault();
// get the first three named "Shakespeare:
var threeShakesPeares = allSpecialHierarchies
.Where(hierarchyLevel => hierarchyLevel.Name == "Shakespeare")
.Take(3)
For better usage you should provide a version that has a parameter IQualityComparer<LevelType>. Let the function above call that one.
And a nice challenge: to be fully LINQ compatible, create a version with a predicate that returns a type T and an equality comparer for this type T, so that you can have all HierarchyLevels with a certain Name, or Abbreviation.
You can Solve your with the help of recursion consider example below :- I have taken sample type of String you can use any of your Type
List<List<HierarchyLevel>> sample = new List<List<HierarchyLevel>>();
Type yourType = typeOf(string);
List<HierarchyLevel> filtered = sample.Where(x => ContainsElement(x, yourType));
public void bool ContainsElement(List<HierarchyLevel> list,Type yourType)
{
if(list.Any(x => x.LevelType == yourType) //check if current node has same level type
return true;
else if(list.Childern.Count > 0) //check if current node has children if yes then call ContainsElement again
return list.Children.Any(x => ContainsElement(x,yourType));
else
return false; //else return false in last
}
Thanks to user743414 for pointing out how simple this was :)
By using a dictionary instead, I could reference to the specific list. (This is also a faster option.)
Dictionary<Type,List<HierarchyLevel>> HierarchicalData;
I can now use it with a key of 'Type':
private void UpdateGeneralData(object Entity, Dictionary<Type,List<HierarchyLevel>> TypeData)
{
CBType.Items.Clear();
foreach (var item in TypeData[Entity.GetType()])
{
CBType.Items.Add(item);
}
}
Something like this (?):
List<HierarchyLevel> var = hLevel.Select(h => h.Children.Where(c => c.Param = "desired param")).ToList();

Looping through a List

Ok, i have a class which is as follows:
public class MT101
{
// Sequence A
public string tag20 { get; set; } // 16x
public string tag21R { get; set; } // 16x
public string tag28D { get; set; } // 16x
public List<String> tag50; // Option F, G, H, C, or L
}
i then have a method, which make a new instance of said class as follows:
public class CheckMessage
{
private List<MT101> Message101 = new List<MT101>();
public List<MT101> CheckMt101Message(string[] messageBody)
{
MT101 buildMessage = new MT101();
.
.
. perform all the neccessary logic to add into buildMessage and then return the
. Message101 object
Message101.Add(buildMessage);
return Message101;
}
}
I call the Method from another class as such:
GlobalClasses.CheckMessage gm = new GlobalClasses.CheckMessage();
and pass in the variables as such:
string[] _block4 = Regex.Split(trimmed, #"\r\n");
gm.CheckMt101Message(_block4);
foreach (GlobalClasses.CheckMessage item in gm)
{ }
In visual studio, i get the following errors when compiled:
Error 1 GlobalClasses.MT101.System.Collections.IEnumerable.GetEnumerator()': containing type does not implement interface 'System.Collections.IEnumerable'
Error 2 foreach statement cannot operate on variables of type 'GlobalClasses.CheckMessage' because 'SASMI.GlobalClasses.CheckMessage' does not contain a public definition for 'GetEnumerator'
I've googled and found reference to setting IEnumerable to the class, so 'ive tried this:
public class Mt101 : IEnumberable
same message being reported.
I guess you want to iterate over the MT101 objects in the Message101 field in the class. The easiest way to accomplish this, is to make Message101 public, e.g.:
public class CheckMessage
{
private List<MT101> message101 = new List<MT101>();
public List<MT101> Messge101 { get { return message101; } }
public List<MT101> CheckMt101Message(string[] messageBody)
{
MT101 buildMessage = new MT101();
.
.
. perform all the neccessary logic to add into buildMessage and then return the
. Message101 object
Message101.Add(buildMessage);
return Message101;
}
}
And then you can iterate over these objects:
foreach (MT101 item in gm.Message101)
{
// ...
}
You are ignoring the return value of CheckMt101Message and instead try to iterate on the class itself. That won't work. Save the return value and iterate over it:
var messages = gm.CheckMt101Message(_block4);
foreach (Mt101 item in messages)
{
...
}
this is one of the procedures to iterate through the loop
EMP oeemp= new EMP();
List<EMP> tr= new List<EMP>();
foreach(var t in tr)
{
}
If you want to iterate on the internal List using the reference to the class CheckMessage then you need to implement a function that returns an IEnumerator like this
in CheckMessage class
public IEnumerator<MT101> GetEnumerator()
{
foreach (MT101 m in this.Message101)
{
yield return m;
}
}
in your code
foreach(MT101 m in gm)
{
.....
}
Of course you could execute the foreach also on the return value of CheckMt101Message but the yield keyword allows a couple of advantages. You need to have just the reference to the CheckMessage class, you don't need to make your internal list public, you could execute the loop at any moment using only the CheckMessage reference and you could add custom code before and after the yeld.
You have to do
foreach (MT101 item in gm.CheckMt101Message(_block4)) { ... }
I'm afraid you have a big confusion so I recommend you to check yourself about why your code is not working, this would be much more helpful for you than just copy paste any of provided solutions.

Generic type's causing issue C#.net

I have class called GroupItem, i can store any type here say int, string, decimal, datetime etc.., Then, i have GroupItems which will store any groupItem. I'm using an arraylist to store all the groupItem.
public class GroupItem<T>
{
private string heading;
private List<T> items = new List<T>();
public GroupItem() { }
public string Heading
{
get { return heading; }
set { heading = value; }
}
public List<T> Items
{
get { return items; }
set { items = value; }
}
public void Add(T value)
{
this.items.Add(value);
}
public T this[int index]
{
get
{
return this.items[index];
}
}
}
public class GroupItems
{
private string groupName;
private List<object> items = new List<object>();
public string GroupName
{
get { return groupName; }
set { groupName = value; }
}
public GroupItems() { }
public void Add(object value)
{
this.items.Add(value);
}
public object this[int index]
{
get
{
return this.items[index];
}
}
}
I want to retrieve from GroupItems. How i can get generic item's values in groupItems?
I'm now inserting two items, datetime and int to groupitems. Now i want to retrieve groupitems[2] value but how i can convert this to groupItem without knowing what it is. Even we may get its genericarguments by getType().getGenericarguments()[0]. But how i can create an instance based upon that.
If the list is storing heterogeneous items, then I would suggest you need a common non-generic interface or base-class. So, say we have
interface IGroupItem {
// the non-generic members, and maybe
// "object Value {get;}" etc, and maybe
// "Type ItemTypr {get;}"
}
You would then have:
class GroupItem<T> : IGroupItem {...}
an you would then use
List<IGroupItem> ...
instead of ArrayList, or, franky, in place of GroupItems {...}
What I'd do is create a generic collection such as:
public class GroupItems<T> : List<GroupItem<T>>
{
}
If you need to extend the basic functionality of a list, you could also extend Collection<T> and override the methods you need:
public class GroupItems<T> : Collection<GroupItem<T>>
{
protected override void InsertItem(int index, T item)
{
// your custom code here
// ...
// and the actual insertion
base.InsertItem(index, item);
}
}
How about just replacing your GroupItems class with List<GroupItem<T>> ?
Depending on what you do with GroupItem you should either inherit from List/Collection as was offered by other or use a generic collection inside your class
e.g.
class GroupItem<T>
{
private List<T> items = new List<T>();
public void Add(T value)
{
items.Add(value);
}
public T Get()
{
//replace with some logic to detemine what to get
return items.First();
}
}
There are two situations that could be covered by your question:
You want to simply store a collection of GroupItem's of type T in the class GroupItems.
You want to store a collection of generic GroupItem's of any type in the class GroupItems. To better clarify, I mean that you could store GroupItem<DateTime> or GroupItem<int> in the same GroupItems class.
Here are some ways of going about storing and retrieving for both scenarios:
Same Type
public class GroupItem<T>
{
// ... Code for GroupItem<T>
}
public class GroupItems<T>
{
private List<GroupItem<T>> mItems = new List<GroupItem<T>>();
public void Add(T item)
{
mItems.Add(item);
}
public T GetItem(int index)
{
return mItems[index];
}
}
Here you will build a collections that contain GroupItem's of the same time, so a collection of GroupItem<DateTime> for example. All the items will be of the same type.
Generic Type
public interface IGroupItem
{
// ... Common GroupItem properties and methods
}
public class GroupItem<T>
{
// ... Code for GroupItem<T>
}
public class GroupItems
{
private List<IGroupItem> mItems = new List<IGroupItem>();
public void Add(IGroupItem item)
{
mItems.Add(item);
}
// This is a generic method to retrieve just any group item.
public IGroupItem GetItem(int index)
{
return mItems[index];
}
// This is a method that will get a group item at the specified index
// and then cast it to the specific group item type container.
public GroupItem<T> GetItem<T>(int index)
{
return (GroupItem<T>)mItems[index];
}
}
Here you will be able to build and maintain a single collection that can contain any GroupItem with any Type. So you could have a GroupItems collection that contains items of GroupItem<DateTime>, GroupItem<int>, etc.
Please note that none of these code examples take into account any erroneous circumstances.
Consider: you have a collection of items; the items may have any runtime type (string, int, etc.). Because of this, the static type of the collections items must be object.
It seems that you want to be able to retrieve items from the list with strong static typing. That's not possible without a lot of conditional logic (or reflection). For example:
object item = collection[0];
if (item is int)
//do something with an int
else if (item is string)
//do something with a string
Now suppose instead of "doing something" with the value of collection[0], we assign the value to a variable. We can do one of two things:
use the same variable for both cases, in which case the static type must be object.
use separate variables, in which case the static type will be string or int, but outside of the conditional logic, we can't know which variable holds the value of collection[0].
Neither option really solves the problem.
By creating GroupItem<T>, you add a level of indirection to this problem, but the underlying problem is still there. As an exercise, try reworking the example, but starting from "Consider: you have a collection of items; the items are of type GroupItem<T> where T may be any runtime type (string, int, etc.)."
Thanks for your inputs.
I have resolved it myself using multiple overloading methods to resolve this.
for example:
private void Print(GroupItem<string> items)
{
///custom coding
}
private void Print(GroupItem<int> items)
{
///custom coding
}
Though its not efficient enough, i want to do in this way as it was .net 2.0.
I'm now improving this in .Net 4.0 with new algorithm.
Thanks a lot for all of your helps.

Categories

Resources