Using string array to set data members' values - c#

I have a class named Person.
Which has data members as follows:
Boolean Old
Boolean Educated
Boolean Employed
Boolean Married
Now I have a string array 'Values' which contains some of these data members.
My task is to set data members present in an array 'Values' to true for a particular object of class Person.
As in I would be having one array for each of the objects p1,p2,p3... of class Person.
For now I am using 'if' condition for each of the data members to check whether they are present in an array. And setting them to true if they do.
How can I use these string values to set data members to true in more efficient way?
Eg. p1 and p2 are objects of class Person
For p1
Values = {Old, Employed}
In this case I will set p1.Old and p1.Employed to true.
For p2
Values= {Employed,Married}
In this case I will set p2.Employed and p2.Married to true.
Person setMembers(string[] Values)
{
var p1 = new Person();
string s1 = "Old";
int i1 = Array.IndexOf(Values, s1);
string s2 = "Educated";
int i2 = Array.IndexOf(Values, s2);
string s3 = "Employed";
int i3 = Array.IndexOf(Values, s3);
string s4 = "Married";
int i4 = Array.IndexOf(Values, s4);
if (i1 > -1)
{p1.Old = true;};
if (i2 > -1)
{p1.Educated = true;};
if (i3 > -1)
{p1.Employed = true;};
if (i4 > -1)
{p1.Married = true;};
return p1;
}

One way to optimize conversion of string values to setting properties would be a hash dictionary that maps strings to actions, like this:
private static readonly IDictionary<string,Action<Person>> ActionByName =
new Dictionary<string,Action<Person>>() {
{"Educated", p => { p.Educated = true; } }
, {"Employed", p => { p.Employed = true; } }
, {"Married", p => { p.Married = true; } }
, {"Old", p => { p.Old = true; } }
};
With this dictionary in hand, you could process the values like this:
Person p1 = ...
foreach (var val in valuesForP1) {
Action<Person> a;
if (ActionByName.TryGetValue(val, out a)) {
a(p1);
}
}
This way lookup of the thing to do is performed using hash table. Note that you can skip the dictionary and use switch instead, like this:
foreach (var val in valuesForP1) {
switch(val) {
case "Educated": p1.Educated = true; break;
case "Employed": p1.Employed = true; break;
case "Married": p1.Married = true; break;
case "Old": p1.Old = true; break;
}
}

If you can use System.Linq, then you would have Contains function and then you can do something like this
objPerson.Old = Values.Contains("Old");
objPerson.Educated = Values.Contains("Educated");

With reflection... but the string values have to match exactly the property names
class Program
{
static void Main(string[] args)
{
var testPerson = CreatePerson(new[] { "Old", "Employed" });
Console.WriteLine(testPerson.Old);
Console.WriteLine(testPerson.Educated);
Console.WriteLine(testPerson.Employed);
Console.WriteLine(testPerson.Married);
}
public static Person CreatePerson(string[] args)
{
Person result = new Person();
Type personType = typeof(Person);
foreach (string item in args)
{
personType.GetProperty(item).SetMethod.Invoke(result, new object[] { true });
}
return result;
}
}
public class Person
{
public bool Old { get; set; }
public bool Educated { get; set; }
public bool Employed { get; set; }
public bool Married { get; set; }
}

I think you may write a method that sets your properties accordingly:
class Person {
bool old, educated, employed, married;
void SetValues(string[] values) {
this.old = values.Contains("Old");
this.educated = values.Contains("Educated");
this.employed = values.Contains("Employed");
this.married = values.Contains("Married");
}
Now you may call this as follows:
Person p1 = new Person();
p.SetValues(new[] { "Old", "Married" };

Related

Simplest method to prove that the contents of two lists (containing objects) are equal

I am having a bit of a frustrating time finding a simple method to compare and prove that the contents of two lists are equal. I have looked at a number of solutions on stackoverflow but I have not been successful. Some of the solutions look like they will require a large amount of work to implement and do something that on the face of it to my mind should be simpler, but perhaps I am too simple to realize that this cannot be done simply :)
I have created a fiddle with some detail that can be viewed here: https://dotnetfiddle.net/cvQr5d
Alternatively please find the full example below, I am having trouble with the object comparison method (variable finalResult) as it's returning false and if the content were being compared I would expect the value to be true:
using System;
using System.Collections.Generic;
using System.Linq;
public class ResponseExample
{
public Guid Id { get; set; } = Guid.Parse("00000000-0000-0000-0000-000000000000");
public int Value { get; set; } = 0;
public string Initials { get; set; } = "J";
public string FirstName { get; set; } = "Joe";
public string Surname { get; set; } = "Blogs";
public string CellPhone { get; set; } = "0923232199";
public bool EmailVerified { get; set; } = false;
public bool CellPhoneVerified { get; set; } = true;
}
public class Program
{
public static void Main()
{
var responseOne = new ResponseExample();
var responseTwo = new ResponseExample();
var responseThree = new ResponseExample();
var responseFour = new ResponseExample();
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseThree);
objectListTwo.Add(responseFour);
bool result = objectListOne.Count == objectListTwo.Count();
Console.WriteLine($"Count: {result}");
bool finalResult = ScrambledEquals<ResponseExample>(objectListOne, objectListTwo);
Console.WriteLine($"Object compare: {finalResult}");
}
//https://stackoverflow.com/a/3670089/3324415
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
var cnt = new Dictionary<T,
int>();
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
}
As people in comments have pointed out this will not work as comparing a complex type by default compares whether the reference is the same. Field by field comparison will not work without implementing equality methods (and then you would need to overload GetHashCode and so on). See https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-5.0
However, if you can use c# 9, which is what you have in the fiddle you can define the type as a record instead of class. Records have built in field by field comparison. See https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#characteristics-of-records
So public class ResponseExample would become public record ResponseExample and your code works as you expect.
Use Enumerable.All<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) Method which Determines whether all elements of a sequence satisfy a condition.
Once you have initilized your two List
list1.All(x=>list2.Contains(x))
This works by ensuring that all elements in list2 are containted in list1 otherwise returns false
Your method as is will compare if the 2 lists contain the same objects. So it is returning false as there are 4 different objects. If you create your list like this, using the same objects, it will return true:
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseTwo);
objectListTwo.Add(responseOne);
To get a true value when the contents of the objects are the same you could serialize the objects into a json string like this:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
JavaScriptSerializer json = new JavaScriptSerializer();
var cnt = new Dictionary<string,
int>();
foreach (T _s in list1)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T _s in list2)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
If the performance is not a big deal, you can use Newtonsoft.Json. We will be able to compare different types of objects as well as run a deep equals check.
First install the package:
Install-Package Newtonsoft.Json
Here is the code snip:
public static bool DeepEqualsUsingJson<T>(IList<T> l1, IList<T> l2)
{
if (ReferenceEquals(l1, l2))
return true;
if (ReferenceEquals(l2, null))
return false;
if (l1.Count != l2.Count)
return false;
var l1JObject = l1.Select(i => JObject.FromObject(i)).ToList();
var l2JObject = l2.Select(i => JObject.FromObject(i)).ToList();
foreach (var o1 in l1JObject)
{
var index = l2JObject.FindIndex(o2 => JToken.DeepEquals(o1, o2));
if (index == -1)
return false;
l2JObject.RemoveAt(index);
}
return l2JObject.Count == 0;
}

c#: collections with unique elements

Is there a collection in C# that guarantees me that I will have only unique elements? I've read about HashSet, but this collection can contain duplicates. Here is my code:
public class Bean
{
public string Name { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
var bean = obj as Bean;
if (bean == null)
{
return false;
}
return this.Name.Equals(bean.Name) && this.Id == bean.Id;
}
public override int GetHashCode()
{
return Name.GetHashCode() * this.Id.GetHashCode();
}
}
You may complain about using non-readonly properties in my GetHashCode method, but this is a way of doing (not the right one).
HashSet<Bean> set = new HashSet<Bean>();
Bean b1 = new Bean {Name = "n", Id = 1};
Bean b2 = new Bean {Name = "n", Id = 2};
set.Add(b1);
set.Add(b2);
b2.Id = 1;
var elements = set.ToList();
var elem1 = elements[0];
var elem2 = elements[1];
if (elem1.Equals(elem2))
{
Console.WriteLine("elements are equal");
}
And in this case, my set contains duplicates.
So is there a collection in C# that guarantees me that it does not contains duplicates?
So is there a collection in C# that guarantees me that it does not
contains duplicates?
There is no existing collection class in C# that does this. You could write your own, but there is no existing one.
Some extra information regarding the issue you are experiencing
If you change a HashSet entry after adding it to the HashSet, then you need to regenerate the HashSet. My below RegenerateHashSet can be used to do that.
The reason you need to regenerate is that duplicate detection only occurs at insertion time (or, in other words, it relies on you not changing an object after you insert it). Which makes sense, if you think about it. The HashSet has no way to detect that an object it contains has changed.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public static class HashSetExtensions
{
public static HashSet<T> RegenerateHashSet<T>(this HashSet<T> original)
{
return new HashSet<T>(original, original.Comparer);
}
}
public class Bean
{
public string Name { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
var bean = obj as Bean;
if (bean == null)
{
return false;
}
return Name.Equals(bean.Name) && Id == bean.Id;
}
public override int GetHashCode()
{
return Name.GetHashCode() * Id.GetHashCode();
}
}
public class Program
{
static void Main(string[] args)
{
HashSet<Bean> set = new HashSet<Bean>();
Bean b1 = new Bean { Name = "n", Id = 1 };
Bean b2 = new Bean { Name = "n", Id = 2 };
set.Add(b1);
set.Add(b2);
b2.Id = 1;
var elements = set.ToList();
var elem1 = elements[0];
var elem2 = elements[1];
if (elem1.Equals(elem2))
{
Console.WriteLine("elements are equal");
}
Console.WriteLine(set.Count);
set = set.RegenerateHashSet();
Console.WriteLine(set.Count);
Console.ReadLine();
}
}
}
Note that the above technique is not bullet-proof - if you add two objects (Object A and Object B) which are duplicates and then change Object B to be different to Object A then the HashSet will still only have one entry in it (since Object B was never added). As such, what you probably want to do is actually store your complete list in a List instead, and then use new HashSet<T>(yourList) whenever you want unique entries. The below class may assist you if you decide to go down that route.
public class RecalculatingHashSet<T>
{
private List<T> originalValues = new List<T>();
public HashSet<T> GetUnique()
{
return new HashSet<T>(originalValues);
}
public void Add(T item)
{
originalValues.Add(item);
}
}
If you don't write your own collection type and handle property changed events to re-evaluate the items, you need to re-evaluate the items at each access. This can be accomplished with LINQ deferred execution:
ICollection<Bean> items= new List<Bean>();
IEnumerable<Bean> reader = items.Distinct();
Rule: only use items to insert or remove elements, use reader for any read access.
Bean b1 = new Bean { Name = "n", Id = 1 };
Bean b2 = new Bean { Name = "n", Id = 2 };
items.Add(b1);
items.Add(b2);
b2.Id = 1;
var elements = reader.ToList();
var elem1 = elements[0];
var elem2 = elements[1]; // throws exception because there is only one element in the result list.

Check if multiple bool variables have specific value

In my current app I have 6 players and everyone has 1 boolean variable. Which under certain circumstances are set to true (originally they are false).. The problem is that I want to check which 5 variables are set to true and which one is set to false but I can't come up with any good idea.. only some if statements checking every single combination
if(a && b && c && d && e && !f)
{
//f is false in this case and I will do some operations here
}
However this is the ugliest and not well written code ever. What would be more general way of doing it?
You're going to have a hard time doing this with just booleans. But if you wrap the boolean in a class with some other data, it becomes easier.
class Item
{
public bool IsCondition {get; set;}
public string Name {get; set;}
}
var itemsToCheck = new List<Item>()
{
new Item { IsCondition = true; Name = "A",
new Item { IsCondition = true; Name = "B",
new Item { IsCondition = false; Name = "C",
new Item { IsCondition = true; Name = "D",
}
foreach(var item in itemsToCheck)
{
if(!Item.IsCondition)
{
Console.WriteLine($"Item {item.Name} is false");
}
}
You can also get a list of all those that are false with Linq
var items = itemsToCheck.Where(i => !i.IsCondition);
Or if you know there will only ever be one that is false, you can get that single item.
var item = itemsToCheck.Where(i => !i.IsCondition).Single();
So there's two takeaways from this:
You should store sets of similar data in a collection, such as a List
Use a class when you want to group some information together.
You can assign them boolean list and then work with them.
List<bool> bools = new List<bool> {a,b,c,d,e,f};
if (bools.Count(x => x) == 5) // if there are 5 true items
{
int index = bools.IndexOf(false); // index of false item
// do your things here.
}
Remember that indexes are 0 based. means that index 0 refers to first item.
Normally you'd use array/list and just count false values:
var onlyOneFromListIsFalse = players.Select(p => !p.SomeProperty).Count() == 1;
You can use similar approach with individual variables
var onlyOneVariableIsFalse = ((a ? 0 : 1) + (b ? 0 : 1) ... (f ? 0 : 1)) == 1;
Using LINQ and List/Array will greatly reduce your code.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var players = new List<Player>
{
new Player("Orel", true),
new Player("Zeus"),
new Player("Hercules", true),
new Player("Nepton"),
};
var playingPlayers = players.Where(p => p.IsPlaying);
foreach (var player in playingPlayers)
{
Console.WriteLine(player.Name);
}
}
}
public class Player
{
public string Name { get; set; }
public bool IsPlaying { get; set; }
public Player(string name, bool isPlaying = false)
{
Name = name;
IsPlaying = isPlaying;
}
}

Is there a var.contains - C#

I have a question about the following code:
private void Filter (object sender, Android.Text.TextChangedEventArgs e)
{
List<Animal> animalList = new List<Animal>();
if(!string.IsNullOrEmpty(_editText.Text))
{
foreach (string str in _animalList)
{
if (str.Contains(_editText.Text))
{
animalList.Add (str);
}
}
}
_listView.Adapter = new AnimalAdapter(this, _animalList = animalList);
}
The Animal class:
public class Animal
{
private readonly int _intKey;
public int AnimalNumber { get; private set; }
public int StableNumber { get; private set; }
public int LactoseNumber { get; private set; }
public Animal ( int intKey, int animalNumber, int stableNumber, int lactoseNumber )
{
_intKey = intKey;
AnimalNumber = animalNumber;
StableNumber = stableNumber;
LactoseNumber = lactoseNumber;
}
public override string ToString ()
{
return "Number: " + AnimalNumber + "\nGroup: " + StableNumber + "\nLactation: " + LactoseNumber;
}
}
Declaration of _animalList:
private List<Animal> _animalList;
i need to check if the _animalList Contains the input of the _editText.Text.
But _animalList isn't a string so i need to use a var.
Is there something like a var.Contains or do i have to use something else?
Contains method is available for string type. You will need to cast your object to string.
A/c to your class definition you should do like:
foreach (Animal str in _animalList)
{
if (str.ToString().Contains(_editText.Text)) //using user defined "ToString()"
{
animalList.Add (str);
}
}
You can also check individual properties:
foreach (Animal str in _animalList)
{
if (str.AnimalNumber.ToString().Contains(_editText.Text)) //if "AnimalNumber" is like "_editText.Text"
{
animalList.Add (str);
}
}
Instead of trying to filter using ToString, it would be better to use the real property values. For example:
var number = Convert.ToInt32(_editText.Text);
var filteredList = _animalList
.Where(x => x.AnimalNumber == number ||
x.StableNumber == number ||
x.LactoseNumber == number)
.ToList();
Otherwise, user could type "Number" and since your ToString override contains that string, all of the items in the list would match positively.
(I didn't include any validation or error checking in the code above, so you should consider those as well).
var inputText = _editText.Text;
int enteredNumber;
// you should make sure that the inputText is always an int
var isInt = int.TryParse(inputText, out enteredNumber);
//for example, if you are going to find by AnimalNumber, which is an int, you can use this. .
if (isInt){
foreach (var animal in _animalList){
var animalNumber = animal.AnimalNumber;
if (animalNumber == enteredNumber)
{
animalList.Add(animal);
}
}
}
Edit (LINQ alternative):
if (isInt){
animalList.AddRange(from animal in _animalList
let animalNumber = animal.AnimalNumber
where animalNumber == enteredNumber
select animal);
}
_animalList.Select(a => a.ToString()).Contains(_editText.Text)
This expression returns true if the output of the ToString method of any animal object equals _editText.Text.
_animalList.Select(a => a.ToString()).Any(str => str.Contains(_editText.Text))
This expression returns true if the output of the ToString method of any animal object contains _editText.Text (as a substring). This is equivalent to Shaharyar's answer.
var animalList = _animalList.Where(a => a.ToString().Equals(_editText.Text)).ToList();
var animalList = _animalList.Where(a => a.ToString().Contains(_editText.Text)).ToList();
These statements filter the input list directly.

Acessing object through object array

I want to learn classes atm and here is what I came up with:
Class level to create a level. This class has an object array that fills itself with rooms (raeume) which is the other class. Now, I want to access the objects in the object array from room, after I inserted them. Here is what I want to type:
wohnung.rooms[i].raumname.toString();
Here are the two classes
class raum
{
static object[] o_nebenraum = new object[3]; //N-O-S-W
static string s_raumname = "";
public object[] nebenraume
{
get
{
return o_nebenraum;
}
set
{
o_nebenraum = value;
}
}
public string raumname
{
get
{
return s_raumname;
}
set
{
s_raumname = value;
}
}
}
class level
{
static object[] o_rooms = new object[100];
public object[] rooms
{
get
{
return o_rooms;
}
set
{
o_rooms = value;
}
}
}
Here is how I set everything up.
level wohnung = new level();
raum keller = new raum();
raum wohnzimmer = new raum();
raum kueche = new raum();
raum schlafzimmer = new raum();
wohnung.rooms[0] = keller;
wohnung.rooms[1] = wohnzimmer;
wohnung.rooms[2] = kueche;
wohnung.rooms[3] = schlafzimmer;
keller.raumname = "Keller";
wohnzimmer.raumname = "Wohnzimmer";
kueche.raumname = "Küche";
schlafzimmer.raumname = "Schlafzimmer";
for (uint i = 0; i < 3; i++)
{
Console.WriteLine("Wohnung beinhaltet jetzt " + *MISSING CODE PART, I WANT TO GET THE .raumname out of the object array from wohnung.room*);
}
Console.ReadKey();
You have to use generic typed list List<T> (See on MSDN) instead of array, in this case you'll have the indexed access for the typed list elements
So instead of:
static object[] o_rooms = new object[100];
public object[] rooms
Use:
static IList<raum> o_rooms = new List<Raum>(100);
public IList<raum> rooms
Try this(in the for Loop):
Console.WriteLine("Wohnung beinhaltet jetzt " + (wohnung.rooms[i] as raum).raumname );
You would be better off using generics though in which case the class level would now look like:
class level
{
static List<raum> o_rooms = new List<raum>();
public List<raum> rooms
{
get { return o_rooms; }
set { o_rooms = value; }
}
}
and the for loop can be replaced with a foreach loop as follows:
foreach(raum room in wohnung.rooms)
{
Console.WriteLine("Wohnung beinhaltet jetzt " + room.raumname );
}

Categories