Checking for double booking in arrays - c#

Ok, my project to create an event organizer for a music contest. Basically I take input data such as a list of about 400 entries and I output into a few separate excel sheets by room number and type. The problem is, when assigning events to an array, (I have a separate array for each room and all the arrays are contained in a list) I need to check and fix if accompanists are double booked for the same time slot. Or essentially, if two different arrays have the same accompanist value for array[i]
Ideas? Please try to explain well, I'm self taught with only a bit of experience
private void assignEvents()
{
// assign every event in order
// check accompanists for no double-books
foreach (Event[] room in roomList)
{
int i = 0;
foreach (Event ev in eventList)
{
if (ev.Type == room[0].Type)
{
room[i] = ev;
i++;
}
}
}

You could do it with LINQ like Tejs suggests, but I would go another route since you are designing an organizer and you will probably need the extra information any way (for example, to display the schedule of an accompanist). I would have a list of all accompanists with a sorted array of all of their bookings (basically , their schedule). You can then have a function to check for every new booking whether there is a conflict (in the booking array) for each of the accompanists in the booking and if not, continue by adding the booking to the accompanists' schedule

If I understand you correctly, you must compare values per column. I suggest to create your own RoomList collection class by deriving it from your actual collection type and to add it the possibility to iterate columns
public class RoomList : List<Event[]>
{
public IEnumerable<Event> TimeSlot(int columnIndex)
{
foreach (Event[] events in this) {
if (events != null && columnIndex < events.Length) {
yield return events[columnIndex];
}
}
}
}
Then you can use LINQ to get double bookings
RoomList roomList = new RoomList();
// ...
for (int slot = 0; slot < roomList[0].Length; slot++) {
var doubleBooked = roomList.TimeSlot(slot)
.GroupBy(e => e.Type)
.Where(g => g.Count() > 1);
if (doubleBooked.Any()) {
Console.WriteLine("Doubly booked accompanists in slot {0}", slot);
foreach (var accompanistGroup in doubleBooked) {
Console.WriteLine(" {0}", accompanistGroup.Key);
}
}
}

Related

If it possible accelerate From-Where-Select method?

I have two var of code:
first:
struct pair_fiodat {string fio; string dat;}
List<pair_fiodat> list_fiodat = new List<pair_fiodat>();
// list filled 200.000 records, omitted.
foreach(string fname in XML_files)
{
// get FullName and Birthday from file. Omitted.
var usersLookUp = list_fiodat.ToLookup(u => u.fio, u => u.dat); // create map
var dates = usersLookUp[FullName];
if (dates.Count() > 0)
{
foreach (var dt in dates)
{
if (dt == BirthDate) return true;
}
}
}
and second:
struct pair_fiodat {string fio; string dat;}
List<pair_fiodat> list_fiodat = new List<pair_fiodat>();
// list filled 200.000 records, omitted.
foreach(string fname in XML_files)
{
// get FullName and Birthday from file. Omitted.
var members = from s in list_fiodat where s.fio == FullName & s.dat == Birthdate select s;
if (members.Count() > 0 return true;
}
They make the same job - searching user by name and birthday.
The first one work very quick.
The second is very slowly (10x-50x)
Tell me please if it possible accelerate the second one?
I mean may be the list need in special preparing?
I tried sorting: list_fiodat_sorted = list_fiodat.OrderBy(x => x.fio).ToList();, but...
I skip your first test and change Count() to Any() (count iterate all list while any stop when there are an element)
public bool Test1(List<pair_fiodat> list_fiodat)
{
foreach (string fname in XML_files)
{
var members = from s in list_fiodat
where s.fio == fname & s.dat == BirthDate
select s;
if (members.Any())
return true;
}
return false;
}
If you want optimize something, you must leave comfortable things that offer the language to you because usually this things are not free, they have a cost.
For example, for is faster than foreach. Is a bit more ugly, you need two sentences to get the variable, but is faster. If you iterate a very big collection, each iteration sum.
LINQ is very powerfull and it's wonder work with it, but has a cost. If you change it for another "for", you save time.
public bool Test2(List<pair_fiodat> list_fiodat)
{
for (int i = 0; i < XML_files.Count; i++)
{
string fname = XML_files[i];
for (int j = 0; j < list_fiodat.Count; j++)
{
var s = list_fiodat[j];
if (s.fio == fname & s.dat == BirthDate)
{
return true;
}
}
}
return false;
}
With normal collections there aren't difference and usually you use foeach, LINQ... but in extreme cases, you must go to low level.
In your first test, ToLookup is the key. It takes a long time. Think about this: you are iterating all your list, creating and filling the map. It's bad in any case but think about the case in which the item you are looking for is at the start of the list: you only need a few iterations to found it but you spend time in each of the items of your list creating the map. Only in the worst case, the time is similar and always worse with the map creation due to the creation itself.
The map is interesting if you need, for example, all the items that match some condition, get a list instead found a ingle item. You spend time creating the map once, but you use the map many times and, in each time, you save time (map is "direct access" against the for that is "sequencial").

Index out of rage cannot be resolved [ASP.NET Core]

I got the following code:
//getting skills from DB
var skills = this.db.HumanSkills.Where(r => r.HumanId == model.Id).Include(x => x.Skill).ToList();
for (int i = 0; i < model.Skills.Count; i++) //iterating over the list. Boundary set to i < model.Skills.Count this is number of elements in my viewmodel (model.Skills) using which I am going to update Name property of my DB elements.
{
if (skills[i] != null) //index out of range here. Why?
{
//here I update DB skill names using model skill names
skills[i].Skill.Name = model.Skills[i].Skill.Name;
}
else //here I create new Skill for every element of the DB skill list that doesn't exist
{
var newSkill = new HumanSkill { Skill = model.Skills[i].Skill, Human = currentHuman };
this.db.Add(newSkill);
this.db.SaveChanges(); //yes, I know it's not a good practice to execute it each time, but otherwise rows in my HumanSkills table are not sorted.
}
}
On line:
if (skills[i] != null) //index out of range here. Why?
I am getting index out of range, even though I use !=null
It seems to me that var skills is a different object than model.Skills
"var skills" and "model.Skills" are 2 different variables.
There are of the same type, but it's 2 differents objects!
You can do that :
IEnumerable<Skill> skillsList = this.db.HumanSkills.Where(r => r.HumanId == model.Id).Include(x => x.Skill).ToList();
foreach (Skill s in skillsList)
{
// your logic... or debug.print for check how many items are get from the db
}

How to find if 3 elements in list are same in Unity3D

I'm working on a Card game in unity. where I have 5 different cards (A,B,C,D,E) and cards i can have at my hand is maximum 6. And I have a button. So clicking on button once I draw one card out. So all these functions are ready. But i want to perform an action when out of those cards that are drawn. if any three cards are similar.
For eg:- I draw out 5 cards And the drawn cards are in this order: A,A,A,B,C now as you see i have 3-A I want to perform A Action. Or if cards are like this A,B,C,C,C,E if want to perform C function because there are 3 C's. So if 3 cards are same type I want to perform an action related to it.
So to brief more I'll explain the flow once. Card-Draw Button Click -> Keep getting cards per click -> Add the cards to a list called _CardList & Sort the cards in List -> If 3 Same Cards are there perform an action. Im Facing Problem at the last one of checking the card.
I have already done a method but its super huge and not advisable. So if you guys can help me in this that will help me a lot. Thanks and Ill post all my doubts and question below with what i tried.
Try#1
Have multiple list like:
List<Card> Alist;
List<Card> BList; //and so on until And keep creating list as long as card type
.
.
List<Card> Elist;
void DrawButton() // Function thats passed to the button. Meaning what to happen when the button is clicked
{
//Here we instantiate the card randomly from a list given from inspector.
GameObject card = Instantiate(cards._cardModel, _playerHandPoints[clicks].localPosition, _playerHandPoints[clicks].localRotation, mCardHolderParent.transform);
//We access the card script attached to card and give its type and everything
card.GetComponent<Cards>()._cardType = cards._cardType;
//Here we pass the instantiated card
AddNewCard(card.GetComponent<Cards>());
//Every time after adding we check if any list count is 3 and perform related action
CardMatchThreeChecker();
}
AddNewCard(Card inNewCard)
{
//in this function we check the card type and add it to their specific list & also add it to _cardList.
switch (inNewCard._cardType)
{
case CardType.A: AList.Add(inNewCard);
break;
case CardType.B: BList.Add(inNewCard);
break;
case CardType.C: CList.Add(inNewCard);
break;
case CardType.D: DList.Add(inNewCard);
break;
case CardType.E: EList.Add(inNewCard);
break;
default:
break;
}
_CardList.Add(inNewCard);
}
void CardMatchThreeChecker()
{
if (AList.Count == 3)
{
SceneManager.LoadScene(1);
}
if (BList.Count == 3)
{
SceneManager.LoadScene(2);
}
if (CList.Count == 3)
{
SceneManager.LoadScene(3);
}
if (DList.Count == 3)
{
SceneManager.LoadScene(4);
}
if (EList.Count == 3)
{
SceneManager.LoadScene(5);
}
}
Problem - 1: with the above method is if I add any new cards in future i need to keep adding many lists on and on which is not advisable as the cards playable at a time can also be 20 or more
Problem -2 : There is no use of the List "_cardList" that contains all the cards instantiated.
Question - 1 Instead I want to check if any 3 things are similar in the _CardList List itself. Like every time I draw a card they all go and add themself to cardlist's List and I want to check if any three elements are similar in the list then perform the related action. I searched in internet but dint find or get an idea for what to do or how to try.
But Yet I tried a method but now im stuck in that too.Method metioned Below
These lines below come under a function called CardChecking() That is called in DrawButton(). Here Im saying it to check the list only if card count is more than 2 because we have a match only if 3 cards are there and we cant have a match if cards are less that 3 as we are checking if there are 3 similar cards. And as we want to check the elements of the list we have a for loop with (i,i+1,i+2) making it checking elements of the cardlist. Problem of this outside the snippet.
for (int i = 0; i < _CardList.Count; i++)
{
if (_CardList.Count > 2)
{
if(_CardList[i]._cardType == _CardList[i+1]._cardType && _CardList[i + 1]._cardType == _CardList[i+2]._cardType)
{
SceneManager.LoadScene(_CardList[i]._cardType.ToString());
}
else
{
continue;
}
}
}
Problem that I faced is: for example if i draw 3 cards my cardlist count would be 3, which will true my condition and check the function. If my cards are not of same type it goes to next for iteration at that time my i values will be cardList[1] || cardList[2] && cardList[2] || cardList[3] meaning my card values 1,2 & 3 on second iteration for my for-loop but when it checks for cardList[3] it throws Index out of range as my cardlist count is 3 and it doesnt have the 4th element the for loop is checking for. So Dont know How to overcome this.
To solve Problem - 1,you can use Dictionary<CardType,List> :
Dictionary<CardType,List<Card>> cardListDictionary = new Dictionary<CardType, List<Card>>();
AddNewCard(Card inNewCard)
{
if (!cardListDictionary.TryGetValue(inNewCard._cardType,out var list))
{
list = new List<Card>();
cardListDictionary.Add(inNewCard._cardType,list);
}
list.Add(inNewCard);
}
To solve Problem - 2,implement CardChecking like this :
void CardChecking()
{
if (_CardList.Count > 2)
{
var type = _CardList[0]._cardType;
var count = 1;
for (int i = 1; i < _CardList.Count; i++)
{
if(_CardList[i]._cardType == type)
{
count++;
if (count == 3)
{
SceneManager.LoadScene(type.ToString());
}
}
else
{
type = _CardList[i]._cardType;
count = 1;
}
}
}
}
I assume the order of the cards doesn't matter and think you are looking for Linq GroupBy
// Just a simplified example since you didn't show your Card class
class Card
{
public CardType _cardType;
public Card(CardType type)
{
_cardType = type;
}
}
var list = new List<Card>
{
new Card(CardType.A),
new Card(CardType.B),
new Card(CardType.C),
new Card(CardType.C),
new Card(CardType.B),
new Card(CardType.B),
new Card(CardType.A),
new Card(CardType.A),
new Card(CardType.B)
};
// This creates separated groups according to the value of the card
var groups = list.GroupBy(c => c._cardType)
// Then it filters out only those groups that have 3 or more items
.Where(g => g.Count() >= 3);
// Then you can simply iterate through them
foreach (var #group in groups)
{
// The Key is the value the grouping was based on
// so in this example the Card._cardType
Debug.Log($"There are {#group.Count()} cads with cardType = {#group.Key}");
// you can also get all the according cards if needed
//var cards = #group.ToArray();
}
will print
There are 3 cards with cardType = A
There are 4 cards with cardType = B
Further if you just want to get the first group that has 3 items and anyway already know that there will never be two or more at the same time you could go
// again first create separate groups based on the value
var group = list.GroupBy(c => c._cardType)
// Pick the first group that has at least 3 items
// or NULL if there is none
.FirstOrDefault(g => g.Count() >= 3);
if (group != null)
{
Debug.Log($"Found a group of {group.Count()} for the value = {group.Key}!");
}
else
{
Debu.Log("There is no group with 3 elements yet...");
}
Well, not sure if I get the whole picture right. Let me simplify the question, and if it works, then there is a solution.
Question: I have a list of cards, and I need to perform an action if there are 3 instances of the card of the same type in the list.
Answer: Use Linq to group the list by the type. Then, filter the result to find the "3-cards cases".
Here you go:
var answer = _CardList
.GroupBy(c => c.GetCardType())
.ToDictionary(g => g.Key, g => g.Count()) // Count by the card type.
.Where(x => x.Value >= 3) // Three or more cards of the same type.
.Select(x => x.Key) // Fetch the card type.
.FirstOrDefault(); // Only one match is needed.
if (answer != null) {
// answer is the type of the card that is counted 3 or more times.
}
If you need to detect all the "3-cards cases" remove FirstOrDefault and deal with the collection.

Sort List Of Objects By Properties C# (Custom Sort Order)

I currently have a list of objects that I am trying sort for a custom made grid view. I am hoping that I can achieve it without creating several customized algorithms. Currently I have a method called on page load that sorts the list by customer name, then status. I have a customized status order (new, in progress, has issues, completed, archived) and no matter which sort is used (customer, dates, so on) it should sort the status in the correct order. For example:
I have two customers with two orders each, the first customer is Betty White, the second is Mickey Mouse. Currently, Betty has a new order, and a completed order and Mickey has an order in progress and another on that has issues. So the display order should be:
Betty, New :: Betty, Completed
Mickey, In Progress :: Mickey, Has Issues
I am currently using Packages.OrderBy(o => o.Customer).ThenBy(o => o.Status). This works effectively to get the customers sorted, however this doesn't eliminate the custom sorting of the status property.
What would be the most efficient and standards acceptable method to achieve this result?
case PackageSortType.Customer:
Packages = Packages.OrderBy(o => o.Customer).ThenBy(o=>o.Status).ToList<Package>();
break;
I previously created a method that sorted by status only, however it is my belief that throwing the OrderBy into that algorithm would just jumble the status back up in the end.
private void SortByStatus() {
// Default sort order is: New, In Progress, Has Issues, Completed, Archived
List<Package> tempPackages = new List<Package>();
string[] statusNames = new string[5] { "new", "inProgress", "hasIssue", "completed", "archived" };
string currentStatus = string.Empty;
for (int x = 0; x < 5; x++) {
currentStatus = statusNames[x];
for (int y = 0; y < Packages.Count; y++) {
if (tempPackages.Contains(Packages[y])) continue;
else {
if (Packages[y].Status == currentStatus)
tempPackages.Add(Packages[y]);
}
}
}
Packages.Clear();
Packages = tempPackages;
}
Also, I'm not sure if it is relevant or not; however, the Packages list is stored in Session.
EDIT
Thanks to Alex Paven I have resolved the issue of custom sorting my status. I ended up creating a new class for the status and making it derive from IComparable, then created a CompareTo method that forced the proper sorting of the status.
For those who are curious about the solution I came up with (it still needs to be cleaned up), it's located below:
public class PackageStatus : IComparable<PackageStatus> {
public string Value { get; set; }
int id = 0;
static string[] statusNames = new string[5] { "new", "inProgress", "hasIssue", "completed", "archived" };
public int CompareTo(PackageStatus b) {
if (b != null) {
if (this == b) {
return 0;
}
for (int i = 0; i < 5; i++) {
if (this.Value == statusNames[i]) { id = i; }
if (b.Value == statusNames[i]) { b.id = i; }
}
}
return Comparer<int>.Default.Compare(id, b.id);
}
}
Use:
Packages.OrderBy(o => o.Customer).ThenBy(o => o.Status).ToList<Package>();
I'm not sure what exactly you're asking; why can't you use the Linq expressions in your first code sample? There's OrderByDescending in addition to OrderBy, so you can mix and match the sort order as you desire.

Iterate over custom list and Remove item from list conditionally in C#

I want to iterate over a custom list i.e. defined as:
List<CurrentCluster> _curClusters = new List<CurrentCluster>();
IEnumerator<CurrentCluster> _clusIterate = _curClusters.GetEnumerator();
while (_clusIterate.MoveNext())
{
// Error_01: Cannot implicitly convert CurrentCluster to Cluster
Cluster _curClus = _clusIterate.Current; // Cluster is base class while
// CurrentCluster is derived class
// Error_02: Does not contain a definition for GetClusterSize()
if (_curClus.GetClusterSize() == 0)
{
// Error_03: Remove(char) has some invalid arguments.
_clusIterate.ToString().ToList().Remove(_curClus);
}
}
while method GetClusterSize() is defined in class Cluster.cs as:
public int GetClusterSize()
{
return _clusterObjects.Count;
// _clusterObjects is a defined in this class as:
// List<EvoObject> _clusterObjects = new List<EvoObject>();
}
If the size of specific cluster is equal to zero in that cluster list (i.e. _curClusters then to remove that cluster from the list.
How can we iterate over a custom list and remove item from list conditionally?
How about just using List RemoveAll method and doing this?
_curClusters.RemoveAll(_curClus=>_curClus.GetClusterSize() == 0);
You should be able to use a for loop - you have to work backwards because otherwise you would be moving the elements and some would get skipped.
for (int n=_curClusters.Count; n>=0; n--)
{
if (_curClusters[n].GetClusterSize()==0)
{
_curClusters.RemoveAt(n);
}
}
Removing items from a collection using iteration is both advanced and obsolete technique. Use LINQ instead:
_curClusters = _curClusters.Where(c => c.GetClusterSize() > 0).ToList();
Now curClusters contains just "sized clusters", whatever that means.
If you insist to do it through iterations this is the way:
The catch is that you MUST NOT change a collection while iterating over its items. Instead, you can iterate and determine if an item needs to be deleted and mark it somehow - for instance you can add it to another list which contains only items to be deleted. After the first iteration over the original collection, start second one and remove the items from the original, like so:
var toBeRemoved = new List<CurrentCluster>();
foreach (var suspiciousCluster in _curCluseters)
{
if(suspiciousCluster.GetClusterSize() == 0)
{
toBeRemoved.Add(suspiciousCluster);
}
}
foreach (var voidCluser in toBeRemoved)
{
_curCluster.Remove(voidCluster);
}
Again, _curClusters contains just "sized clusters", whatever this might mean.
However I highly recommend the first approach.
I did not understand why you are going with that complexity ... simply you can achieve the goal by below code
List<CurrentCluster> _curClusters = new List<CurrentCluster>();
_curClusters.RemoveAll(i => i.GetClusterSize()== 0);
//OR
for (int i = 0; i < _curClusters.Count; )
{
//If you have some more logical checking with CurrentCluster
//before remove
if (_curClusters[i].GetClusterSize()== 0)
{
_curClusters.Remove(_curClusters[i]);
continue;
}
i++;
}

Categories