Edit - Check is Moved to The Else, Looping & Multiple Object Changes are Now Prime Issue
I need 5 Teams objects To Play Against each other (Each must play against the Other 4 teams)
The Check had to be in the ELSE statement, Now the Only Problem left is the Scores
add and subtract from ALL the teams same time, and i only play once, i need them to loop
so that they all played against everyone
I Started off like this. Yet i don't know how to go about doing this
//This is the list i check against and add players to when the team
//Played against them
List<string> played = new List<string>();
//Teamlist Holds Objects of type Team
//Contructor - (TeamName,Points,Wins,Losses,Draws,totalpoints)
//string, rest are int type
foreach (var item in Teamlist)
{
if (played.Contains(item.Teamname))
{
//Played against them already or is the same name as their own
}
else
{
//Add own Name on Check
played.Add(item.Teamname);
//Looping here against all Teams Entered to be played against
//How do i go about doing this?
//If Team 1 Wins Team 2 - This is just 2 Randoms between 1
//and 10
if (rand1 > rand2)
{
item.WinGame(); //Increments Team win Points
}
else if (rand1 == rand2)
{
item.DrawGame(); //Draw awards no points
}
else
{
item.LoseGame(); //Lose Points
}
}
}
foreach (var item in Teamlist)
{
//This ToString() looks like this
//return string.Format("Team Name : {0} \t {1} \t {2} \t {3} \t
//{4} \t {5}", teamname, points, win, loss, draw, totalpoints);
Console.WriteLine(item.ToString());
}
Maybe because you made a little error:
First you add item.Teamname
//Add own Name on Check
played.Add(item.Teamname);
Then you check if the previously added item.Teammname is within the list -- always true:
if (played.Contains(item.Teamname))
{
EDIT: This is wrong. It would give you both home and away games.
What about taking the Cartesian Product of the list of teams to give you the list of matches to be played. With Linq it's pretty easy:
static void Main(string[] args)
{
var teams = new[]
{
new { Name = "Team 1"},
new { Name = "Team 2"},
new { Name = "Team 3"},
new { Name = "Team 4"},
new { Name = "Team 5"}
};
var matches =
// here we loop over all the items in the teams collection.
// "teamA" is our loop variable.
from teamA in teams
// here we loop over all the items in the teams collection again.
// like a nested foreach (in)
// "teamB" is our loop variable.
from teamB in teams
// this is like an if(teamA.Name != teamB.Name) within our nested foreach loops
where teamA.Name != teamB.Name
// the select says how we want to create our collection of results.
// Here we're creating a new anonymous object containing the two rival teams.
select new { Team1 = teamA, Team2 = teamB };
foreach (var match in matches)
{
var result = PlayMatch(match.Team1, match.Team2);
Console.WriteLine("{0} played {1} The result was {2}",
match.Team1.Name,
match.Team2.Name,
result);
}
}
private static string PlayMatch(object team1, object team2)
{
// Left as an exercise for the OP.
return "...";
}
here lies your problem:
played.Add(item.Teamname);
if (played.Contains(item.Teamname))
The first line adds the team name to the played list, the second line checks if the team name is in the list. This should always be true, so you never get into the else path...
To fix your code, you need to move played.Add(item.Teamname); inside your else branch, so you're only adding the team name to the played collection if they haven't played them.
foreach (var item in Teamlist)
{
//Add own Name on Check
// played.Add(item.Teamname); // <---- Move from here
if (played.Contains(item.Teamname))
{
//Played against them already or is the same name as their own
}
else
{
//Add own Name on Check
played.Add(item.Teamname); // <---- To here
...
Assuming teamList is IList<Team>:
for(int i = 0; i < teamList.Count() - 1; i++)
for(int j = i + 1; j < teamList.Count(); j++)
{
var team1 = teamList[i];
var team2 = teamList[j];
// will execute for each pair once
}
For 5 teams:
0 plays 1,2,3,4
1 plays 2,3,4
2 plays 3,4
3 plays 4
Edit: Method originally played each team twice. Your notion of a played list is actually required. See below.
Edit: Here's another method, which may prove faster.
void PlayRound(Team[] teams)
{
for (int i = 0; i < teams.Length; i++)
for (int j = i + 1; j < teams.Length; j++)
PlayMatch(teams[i], teams[j]);
}
/// <summary>
/// Plays a full round, one match per team.
/// </summary>
void PlayRound(List<Team> teams)
{
List<Team> played = new List<Team>(); // keep track of teams in the outer loop
foreach (Team teamA in teams)
{
foreach (Team teamB in teams)
{
if (teamA == teamB || played.Contains(teamB))
continue;
else
PlayMatch(teamA, teamB);
}
played.Add(teamA); // add outer loop team to ensure one match per pairing
}
}
/// <summary>
/// Plays a match.
/// </summary>
void PlayMatch(Team teamA, Team teamB)
{
int scoreA = rand.Next();
int scoreB = rand.Next();
if (scoreA > scoreB)
{
teamA.Win();
teamB.Lose();
}
else if (scoreA == scoreB)
{
teamA.Draw();
teamB.Draw();
}
else
{
teamB.Win();
teamA.Lose();
}
}
You are missing one loop in your logic.
Based on the code you provided, here is what happens :
Add current Teamname to played list
Check if current Teamname is in played list
You should add another loop between steps 1 and 2 to loop on all teams and execute the game logic.
foreach (var item in Teamlist)
{
//Add own Name on Check
played.Add(item.Teamname);
if (played.Contains(item.Teamname))
{
//Played against them already or is the same name as their own
}
This is always true? You add the name and next you'll try to find it (you just added it....)
played.Add(item.Teamname);
Should be at the end of the foreach scope i guess
Related
Im setting up a treeView list and need to add some new Items. Idea is to create some sort of "billing list" for each day.
First the user selects a date on the left side and get a treeView displayed on the right; containing all inserted Bills.
To do so, the user can select a Level, a name + amount of money to insert to. The money of all children should be summarized in the respective parent's text data be like
Level| Name amount
0| Complete amount xxx€ 1| Shopping 25€ (calculated)
1.1| Aldi 20€ (inserted)
1.2| Lidl 5€ (inserted)
1.2.1| Milka 3€ (inserted)
1.2.2| Bath 2€ (calculated)
1.2.2.1| Toothbrush 1€ (inserted)
1.2.2.2| Soap 1€ (inserted) 2| Car 100€ (calculated)
2.1| Fuel 80€ (inserted)
2.2| washing 20€ (inserted) 3| Dinner
3.1|
....
...
...
so the tree should be extended whenever the user put in(simple pop-up form with 3 textboxes) a new value.
So far, i've created a dictionary of type >
The outer Dictionary is to store the items apart from each other, split into each one date. Every day could have another kind of treestructure; the only one beeing on top of every day is "0| Complete amount".
the inner dictionary contains the level (0, 1.1, 1.2.2.1...) and all entries for this level.
The problems start and end with the sort of this dictionary.
In case it is sorted and will never be touched, everything is ok.
But if anything is not in order, I need either a way to sort the dictionary in a correct way, or to iterate over it in the right way.
1.1 sould be before 1.2 and before 2, but after 1.
Given the new dictionary like
1|
1.1|
2|
1.2|
2.1|
after i do the "orderby" it will be the same structure, but I need it to be
1|
1.1|
1.2|
2|
2.1|
Does anyone know, how to reach this?
Or is there a way to iterate over all items and add them as child-items in the right order? Or any way to auto-sort the treeview by .split('|')[0]? my items always start with "level + |"
Try IComparable :
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
namespace ConsoleApplication131
{
class Program
{
static void Main(string[] args)
{
string[] inputs = {
"0| Complete amount xxx€ 1| Shopping 25€ (calculated)",
"1.1| Aldi 20€ (inserted)",
"1.2| Lidl 5€ (inserted)",
"1.2.1| Milka 3€ (inserted)",
"1.2.2| Bath 2€ (calculated)",
"1.2.2.1| Toothbrush 1€ (inserted)",
"1.2.2.2| Soap 1€ (inserted) 2| Car 100€ (calculated)",
"2.1| Fuel 80€ (inserted)",
"2.2| washing 20€ (inserted) 3| Dinner"
};
SortParagraph sorter = new SortParagraph();
List<SortParagraph> sortParagraphs = sorter.ParsePararaph(inputs);
List<SortParagraph> sortedParagraphs = sortParagraphs.OrderBy(x => x).ToList();
foreach (SortParagraph sortParagraph in sortedParagraphs)
{
Console.WriteLine("Para : '{0}', Titles = '{1}'", string.Join(".", sortParagraph.subParagraphs), string.Join(",", sortParagraph.titles));
}
Console.ReadLine();
}
}
public class SortParagraph : IComparable<SortParagraph>
{
public int[] subParagraphs { get; set; }
public string[] titles { get; set; }
public List<SortParagraph> ParsePararaph(string[] inputs)
{
List<SortParagraph> paragraphs = new List<SortParagraph>();
foreach(string input in inputs)
{
SortParagraph newParagraph = new SortParagraph();
string[] splitParagraph = input.Split(new char[] { '|' }).ToArray();
newParagraph.titles = splitParagraph.Skip(1).ToArray();
newParagraph.subParagraphs = splitParagraph.First().Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Select(x => int.Parse(x)).ToArray();
paragraphs.Add(newParagraph);
}
return paragraphs;
}
public int CompareTo(SortParagraph other)
{
int minSize = Math.Min(this.subParagraphs.Length, other.subParagraphs.Length);
for (int i = 0; i < minSize; i++)
{
if (this.subParagraphs[i] != other.subParagraphs[i])
{
if (this.subParagraphs[i] < other.subParagraphs[i])
{
return -1;
}
else
{
return 1;
}
}
}
if (this.subParagraphs.Length == other.subParagraphs.Length)
{
return 0;
}
else
{
if (this.subParagraphs.Length < other.subParagraphs.Length)
{
return -1;
}
else
{
return 1;
}
}
}
}
}
I marked jdweng's answer as correct, because I've got the missing hint. keyword "Icomparer".
this solution now works fine, unless i dont need to remove any item (I dont need to, because Im goin to rebuild the complete list in case of removal)
class DuplicateKeyComparer<TKey>:IComparer<TKey> where TKey : IComparable
{
public int Compare(TKey x, TKey y)
{
int result = x.CompareTo(y);
if (result == 0)
return 1; // Handle equality as beeing greater
else
return result;
}
}
i have a friendsystem and want to sort it by the Users Worldrank (via PlayFab).
I want the best at the Top and the worst at the bottom.
I tried it with the following Script
private void sortfriendlist()
{
GameObject[] Friendcolumns = GameObject.FindGameObjectsWithTag("Friendcolumn");
for (int i = 0; i < Friendcolumns.Length; i++)
{
//If ranked
if (Friendcolumns[i].name.Substring(0, 1) != "F")
{
//Set Sibling (Rank)
int rank = int.Parse(Friendcolumns[i].name.Substring(0, 1));
Friendcolumns[i].transform.SetSiblingIndex(rank);
}
//If unranked
else
{
Friendswithoutrank[i] = Friendcolumns[i];
}
}
Debug.Log(Friendswithoutrank.Length + " Friends without Rank");
for(int i = 0; i < Friendswithoutrank.Length; i++)
{
Debug.Log(Friendswithoutrank[i].name + " has no Rank");
Friendcolumns[i].transform.SetAsLastSibling();
}
}
But that doesnt work as you can see here
First Im getting all Friends and store them in an Array. After that Im checking if the Player even has a Rank by checking if there is an "F" at the beginning of the GameObjects Name (Friends without a Rank have no Ranknumber in front of the Gameobject Name so the Name start with an "F"). Than Im getting the Rank of the Friend and set the SiblingIndex to the Rank. So normally it should be sorted now as i think. But that idea doesn´t seem to work.
If you have any idea how to fix it or make it better please let me know!
Sorting can actually be done much easier using Linq (also see this thread):
using System.Linq;
using UnityEngine;
private void sortfirendlist()
{
var Friendcolumns = GameObject.FindGameObjectsWithTag("Friendcolumn");
// Use linq to get the Objects with and without Rank (not starting / starting with "F")
// This works a bit similar to sql code
// -> if the "Where" returns true -> object stays in the list
// using string.StartsWidth which is a
// better choice for what you are doing with Substring(0,1)== ...
var withoutRank = Friendcolumns.Where(obj => obj.name.StartsWith("F"));
var withRank = Friendcolumns.Where(obj => !obj.name.StartsWith("F"));
// Sort the ranked by name (again using Linq)
var rankedSorted = withRank.OrderBy(go => go.name);
// Now we have our ordered arrays -> we just have to apply that to the scene
// set sibling index for the ranked
foreach (var t in rankedSorted)
{
// I simply send them one by one to the bottom
// => when we are finished they are all sorted at the bottom
t.transform.SetAsLastSibling();
}
// Do the same for the unranked to also send them to the bottom
Debug.Log(withoutRank.Length + " Friends without Rank");
foreach (var t in withoutRank)
{
Debug.Log(t.name + " has no rank");
t.transform.SetAsLastSibling();
}
// Now the object we sent to the bottom first
// (first element in withRank) should be on top and everything sorted below it
}
I'm using this:
foreach (var item in set)
{
string matchingString = conLines.FirstOrDefault(r => (r.IndexOf(item.firstParam) >= 0 && r.IndexOf(item.secondParam) >= 0) && (r.IndexOf(item.firstParam) < r.IndexOf(item.secondParam)));
}
Where:
List<string> conLines = ...
And
public class className
{
public string firstParam { get; set; }
public string secondParam { get; set; }
}
public static List<className> set = ....
And I want to know at which index of conLines that a matchingString was found.
At the end of the day, what I'm trying to do is search through conLines, string by string, for all matches with firstParam and secondParam (sequentially, in the same string of conLines). If a match is found, I want to change that string in conLines. Whether the line finds a match and gets changed or not, I want to print it out. So essentially I'm reading in conLines and printing it all out including changes in the lines that found a match for firstParam and secondParam.
Example:
if conLines were:
alpha beta dog cat
chair ramp table seat
blog journal article letter
and firstParam, secondParam included:
ramp, table
article, letter
and the changes I made were to add -01 to the matches, I would be printing out:
alpha beta dog cat
char ramp-01 table-01 seat
blog journal article-01 letter-01
No matter how you find the index, it's going to be sub-optimal. You'll wind up enumerating the collection twice. Instead, you should either:
Use a for loop to loop over the collection (if possible). That way, whenever you find the fist match, you'll already have the index (only works if the collection exposes and indexer and a pre-calculated Length/Count property):
for(var i = 0; i < collection.Count; i++)
{
if(collection[i] == query)
{
// You have the match with collection[i] and the index in i
}
}
Loop over the collection using GetEnumerator and count the index using a counter. This will work for any IEnumerable:
var enumerator = collection.GetEnumerator();
var count = 0;
while(enumerator.MoveNext())
{
if(enumerator.Current == query)
{
// You have the match in enumerator.Current and the index in count
}
count++;
}
Either way, you'll only have to loop over the collection a single time rather than twice (once for FirstOrDefault and then again to get the index.
You can do it by using a Select at the front that includes the index:
var temp = conLines.Select((l, i) => new {l, i})
.FirstOrDefault(r => (r.l.IndexOf(item.firstParam) >= 0
&& r.l.IndexOf(item.secondParam) >= 0)
&& (r.l.IndexOf(item.firstParam) < r.l.IndexOf(item.secondParam)
));
string matchingString = temp.l;
int index = temp.i;
Although I'm struggling to figure out how you get the output you want. Why do both lines have a '01'?
I am somewhat struggling with the terminology and complexity of my explanations here, feel free to edit it.
I have 1.000 - 20.000 objects. Each one can contain several name words (first, second, middle, last, title...) and normalized numbers(home, business...), email adresses or even physical adresses and spouse names.
I want to implement a search that enables users to freely combine word parts and number parts.When I search for "LL 676" I want to find all objects that contain any String with "LL" AND "676".
Currently I am iterating over every object and every objects property, split the searchString on " " and do a stringInstance.Contains(searchword).
This is too slow, so I am looking for a better solution.
What is the appropriate language agnostic data structure for this?
In my case I need it for C#.
Is the following data structure a good solution?
It's based on a HashMap/Dictionary.
At first I create a String that contains all name parts and phone numbers I want to look through, one example would be: "William Bill Henry Gates III 3. +436760000 billgatesstreet 12":
Then I split on " " and for every word x I create all possible substrings y that fullfill x.contains(y). I put every of those substrings inside the hashmap/dictionary.
On lookup/search I just need to call the search for every searchword and the join the results. Naturally, the lookup speed is blazingly fast (native Hashmap/Dictionary speed).
EDIT: Inserts are very fast as well (insignificant time) now that I use a smarter algorithm to get the substrings.
It's possible I've misunderstood your algorithm or requirement, but this seems like it could be a potential performance improvement:
foreach (string arg in searchWords)
{
if (String.IsNullOrEmpty(arg))
continue;
tempList = new List<T>();
if (dictionary.ContainsKey(arg))
foreach (T obj in dictionary[arg])
if (list.Contains(obj))
tempList.Add(obj);
list = new List<T>(tempList);
}
The idea is that you do the first search word separately before this, and only put all the subsequent words into the searchWords list.
That should allow you to remove your final foreach loop entirely. Results only stay in your list as long as they keep matching every searchWord, rather than initially having to pile everything that matches a single word in then filter them back out at the end.
In case anyone cares for my solution:
Disclaimer:
This is only a rough draft.
I have only done some synthetic testing and I have written a lot of it without testing it again.I have revised my code: Inserts are now ((n^2)/2)+(n/2) instead of 2^n-1 which is infinitely faster. Word length is now irrelevant.
namespace MegaHash
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class GenericConcurrentMegaHash<T>
{
// After doing a bulk add, call AwaitAll() to ensure all data was added!
private ConcurrentBag<Task> bag = new ConcurrentBag<Task>();
private ConcurrentDictionary<string, List<T>> dictionary = new ConcurrentDictionary<string, List<T>>();
// consider changing this to include for example '-'
public char[] splitChars;
public GenericConcurrentMegaHash()
: this(new char[] { ' ' })
{
}
public GenericConcurrentMegaHash(char[] splitChars)
{
this.splitChars = splitChars;
}
public void Add(string keyWords, T o)
{
keyWords = keyWords.ToUpper();
foreach (string keyWord in keyWords.Split(splitChars))
{
if (keyWord == null || keyWord.Length < 1)
return;
this.bag.Add(Task.Factory.StartNew(() => { AddInternal(keyWord, o); }));
}
}
public void AwaitAll()
{
lock (this.bag)
{
foreach (Task t in bag)
t.Wait();
this.bag = new ConcurrentBag<Task>();
}
}
private void AddInternal(string key, T y)
{
for (int i = 0; i < key.Length; i++)
{
for (int i2 = 0; i2 < i + 1; i2++)
{
string desire = key.Substring(i2, key.Length - i);
if (dictionary.ContainsKey(desire))
{
List<T> l = dictionary[desire];
lock (l)
{
try
{
if (!l.Contains(y))
l.Add(y);
}
catch (Exception ex)
{
ex.ToString();
}
}
}
else
{
List<T> l = new List<T>();
l.Add(y);
dictionary[desire] = l;
}
}
}
}
public IList<T> FulltextSearch(string searchString)
{
searchString = searchString.ToUpper();
List<T> list = new List<T>();
string[] searchWords = searchString.Split(splitChars);
foreach (string arg in searchWords)
{
if (arg == null || arg.Length < 1)
continue;
if (dictionary.ContainsKey(arg))
foreach (T obj in dictionary[arg])
if (!list.Contains(obj))
list.Add(obj);
}
List<T> returnList = new List<T>();
foreach (T o in list)
{
foreach (string arg in searchWords)
if (dictionary[arg] == null || !dictionary[arg].Contains(o))
goto BREAK;
returnList.Add(o);
BREAK:
continue;
}
return returnList;
}
}
}
I have a ListView with two columns, Boxes and Files. I'm adding items to a list of strings, and then populating the ListView with that list of strings. I want to make it so all items that are 8 characters long go into the Boxes column and all items that are 9 characters go into the Files column. So far, I've tried to iterate through using a for loop and utilize an if else statement to add the items, but I seem to be doing something wrong. Here's my current code:
public void PopulateItemsList()
{
BoxAndFileList.Items.Clear();
ScanIdBox.Text = string.Empty;
for (int i = 0; i < BoxNumberRepository._boxAndFileList.Count; i++)
{
var item = BoxNumberRepository._boxAndFileList.Item[i];
if (item.Length == 8)
{
BoxAndFileList.Items.Insert(0, item);
}
else
{
BoxAndFileList.Items.Insert(1, item);
}
}
}
I'm iterating through my list (_boxAndFileList) and trying to utilize Insert() to insert items into the specific index of the columns (Boxes is 0, Files is 1). I can clearly see that Item is a legitimate property of a string list, yet VS keeps saying that list contains no definition of it. How can I go about doing this? And also, I haven't received outside feedback on this way of doing things yet, so if there's a better way, please let me know.
Edit: BoxNumberRepository is a class that news up a list called _boxAndFileList. Code below:
public class BoxNumberRepository : Scan_Form
{
public static List<string> _boxAndFileList = new List<string>();
public void AddItem(string item)
{
_boxAndFileList.Add(item);
}
public void Delete(string item)
{
_boxAndFileList.Remove(item);
}
public IEnumerable<string> GetAllItems()
{
return _boxAndFileList;
}
}
Thanks to Alessandro D'Andria for that suggestion. That was correct. However, all the items are still just adding to the first column, even if they're 9 characters. How can I get 9 character items to add to the second column?
The problem that you are having is that you have to add both the box and file to the list item at the same time.
EDIT: Changed cartesian product to a left outer join.
EDIT: Added comments and fixed a syntax bug
private List<string> _boxAndFileList = new List<string> { "12345678", "123456789", "1234", "123456778" };
public void PopulateItemsList()
{
//clear the list
BoxAndFileList.Items.Clear();
//add the labels to the top of the listbox
BoxAndFileList.Columns.Add("Boxes");
BoxAndFileList.Columns.Add("Files");
//set the view of the list to a details view (important if you try to display images)
BoxAndFileList.View = View.Details;
//clear scan id box
ScanIdBox.Text = string.Empty;
//get all the items whos length are 8 as well as a unique id (index)
var boxes = _boxAndFileList.Where(b => b.Length == 8).Select((b, index) => new { index, b }).ToList();
//get all the items whos length are NOT 8 as well as a unique id (index)
var files = _boxAndFileList.Where(f => f.Length != 8).Select((f, index) => new { index, f }).ToList();
//join them together on their unique ids so that you get info on both sides.
var interim = (from f in files
join b in boxes on f.index equals b.index into bf
from x in bf.DefaultIfEmpty()
select new { box = (x == null ? String.Empty : x.b), file = f.f });
//the real trick here is that you have to add
//to the listviewitem of type string[] in order to populate the second, third, or more column.
//I'm just doing this in linq, but var x = new ListViewItem(new[]{"myBox", "myFile"}) would work the same
var fileboxes = interim.Select(x => new ListViewItem(new []{ x.box, x.file})).ToArray();
//add the array to the listbox
BoxAndFileList.Items.AddRange(fileboxes);
//refresh the listbox
BoxAndFileList.Refresh();
}
Your _boxAndFileList is a List<string> so you should be declare item as string type instead var type:
string item = BoxNumberRepository._boxAndFileList.Item[i];
All your code should be like this:
public void PopulateItemsList()
{
BoxAndFileList.Items.Clear();
ScanIdBox.Text = string.Empty;
for (int i = 0; i < BoxNumberRepository._boxAndFileList.Count; i++)
{
string item = BoxNumberRepository._boxAndFileList.Item[i];
if (item.Length == 8)
{
BoxAndFileList.Items.Insert(0, item);
}
else
{
BoxAndFileList.Items.Insert(1, item);
}
}
}