Removing More Than Two Duplicates From A List - c#

I'm trying to figure out the best way to remove duplicates from a list in C#. However I want to keep two of the duplicated entries (two total identical entries) while removing all of the rest. I've seen plenty of examples of removing all duplicates from a list, but my specific issue seems to be less common.
This is the majority of my code. The background is this is a debate scheduler project for a class I'm in. I research a few ways to delete duplicates and found many on here but not with my stipulations. Each team can only debate twice per day and no more and they are assigned randomly to there debate times leading to my problem of them being assigned more than two times per scheduled day.
The teams are being assigned randomly and then moved to a list, so by duplicates I mean I'm trying to remove all dupes after the initial two if they exist at all.
Thanks and sorry for the ambiguity.
namespace SWEProject3
{
public partial class DebateSchedulerForm : Form
{
//There are 10 slots for team names
//The names can only be edited by the superadmin
static int ADMIN = 1;
static int GUEST = 2;
public List<string> nameList = new List<string>();
public List<string> winList = new List<string>();
public List<string> lossList = new List<string>();
public List<string> dateList = new List<string>();
public List<string> debateList = new List<string>();
public List<string> week1List = new List<string>();
public List<string> week2List = new List<string>();
public List<string> week3List = new List<string>();
public List<string> week4List = new List<string>();
public List<string> week5List = new List<string>();
public List<string> week6List = new List<string>();
public List<string> week7List = new List<string>();
public List<string> week8List = new List<string>();
public List<string> week9List = new List<string>();
public List<string> week10List = new List<string>();
public DebateSchedulerForm(int x)
{
InitializeComponent();
initNames();
initWin();
initLoss();
initDates();
initDebates();
initWeekLists();
ListNames();
ListWin();
ListLoss();
ListDates();
if (x == ADMIN || x == GUEST)
{
Name1.ReadOnly = true;
Name2.ReadOnly = true;
Name3.ReadOnly = true;
Name4.ReadOnly = true;
Name5.ReadOnly = true;
Name6.ReadOnly = true;
Name7.ReadOnly = true;
Name8.ReadOnly = true;
Name9.ReadOnly = true;
Name10.ReadOnly = true;
Win1.ReadOnly = true;
Win2.ReadOnly = true;
Win3.ReadOnly = true;
Win4.ReadOnly = true;
Win5.ReadOnly = true;
Win6.ReadOnly = true;
Win7.ReadOnly = true;
Win8.ReadOnly = true;
Win9.ReadOnly = true;
Win10.ReadOnly = true;
Loss1.ReadOnly = true;
Loss2.ReadOnly = true;
Loss3.ReadOnly = true;
Loss4.ReadOnly = true;
Loss5.ReadOnly = true;
Loss6.ReadOnly = true;
Loss7.ReadOnly = true;
Loss8.ReadOnly = true;
Loss9.ReadOnly = true;
Loss10.ReadOnly = true;
ChangeDates.Visible = false;
Save.Visible = false;
}
}
public void Shuffle()
{
Random gen = new Random();
int n = debateList.Count();
while (n > 1)
{
n--;
int k = gen.Next(n + 1);
string value = debateList[k];
debateList[k] = debateList[n];
debateList[n] = value;
}
}
public void ListNames()
{
this.Name1.Text = nameList[0];
this.Name2.Text = nameList[1];
this.Name3.Text = nameList[2];
this.Name4.Text = nameList[3];
this.Name5.Text = nameList[4];
this.Name6.Text = nameList[5];
this.Name7.Text = nameList[6];
this.Name8.Text = nameList[7];
this.Name9.Text = nameList[8];
this.Name10.Text = nameList[9];
}
public void ListWin()
{
this.Win1.Text = winList[0];
this.Win2.Text = winList[1];
this.Win3.Text = winList[2];
this.Win4.Text = winList[3];
this.Win5.Text = winList[4];
this.Win6.Text = winList[5];
this.Win7.Text = winList[6];
this.Win8.Text = winList[7];
this.Win9.Text = winList[8];
this.Win10.Text = winList[9];
}
public void ListLoss()
{
this.Loss1.Text = lossList[0];
this.Loss2.Text = lossList[1];
this.Loss3.Text = lossList[2];
this.Loss4.Text = lossList[3];
this.Loss5.Text = lossList[4];
this.Loss6.Text = lossList[5];
this.Loss7.Text = lossList[6];
this.Loss8.Text = lossList[7];
this.Loss9.Text = lossList[8];
this.Loss10.Text = lossList[9];
}
public void ListDates()
{
this.Date1.Text = dateList[0];
this.Date2.Text = dateList[1];
this.Date3.Text = dateList[2];
this.Date4.Text = dateList[3];
this.Date5.Text = dateList[4];
this.Date6.Text = dateList[5];
this.Date7.Text = dateList[6];
this.Date8.Text = dateList[7];
this.Date9.Text = dateList[8];
this.Date10.Text = dateList[9];
}
public void initNames()
{
StreamReader sr = new StreamReader("nameList.txt");
for (int i = 0; i < 10; i++)
{
string line = sr.ReadLine();
nameList.Add(line);
}
sr.Close();
}
public void initWin()
{
StreamReader sw = new StreamReader("Win.txt");
for (int i = 0; i < 10; i++)
{
string line = sw.ReadLine();
winList.Add(line);
}
sw.Close();
}
public void initLoss()
{
StreamReader sw = new StreamReader("Loss.txt");
for (int i = 0; i < 10; i++)
{
string line = sw.ReadLine();
lossList.Add(line);
}
sw.Close();
}
public void initDates()
{
StreamReader sw = new StreamReader("Dates.txt");
for (int i = 0; i < 10; i++)
{
string line = sw.ReadLine();
dateList.Add(line);
}
sw.Close();
}
public void initDebates()
{
StreamReader sw = new StreamReader("Debate.txt");
for (int i = 0; i < 45; i++)
{
string line = sw.ReadLine();
debateList.Add(line);
}
sw.Close();
}
public void initWeekLists()
{
week1List.Add(debateList[0]);
week1List.Add(debateList[1]);
week1List.Add(debateList[2]);
week1List.Add(debateList[3]);
week1List.Add(debateList[4]);
week2List.Add(debateList[5]);
week2List.Add(debateList[6]);
week2List.Add(debateList[7]);
week2List.Add(debateList[8]);
week2List.Add(debateList[9]);
week3List.Add(debateList[10]);
week3List.Add(debateList[11]);
week3List.Add(debateList[12]);
week3List.Add(debateList[13]);
week3List.Add(debateList[14]);
week4List.Add(debateList[15]);
week4List.Add(debateList[16]);
week4List.Add(debateList[17]);
week4List.Add(debateList[18]);
week4List.Add(debateList[19]);
week5List.Add(debateList[20]);
week5List.Add(debateList[21]);
week5List.Add(debateList[22]);
week5List.Add(debateList[23]);
week5List.Add(debateList[24]);
week6List.Add(debateList[25]);
week6List.Add(debateList[26]);
week6List.Add(debateList[27]);
week6List.Add(debateList[28]);
week7List.Add(debateList[29]);
week7List.Add(debateList[30]);
week7List.Add(debateList[31]);
week7List.Add(debateList[32]);
week8List.Add(debateList[33]);
week8List.Add(debateList[34]);
week8List.Add(debateList[35]);
week8List.Add(debateList[36]);
week9List.Add(debateList[37]);
week9List.Add(debateList[38]);
week9List.Add(debateList[39]);
week9List.Add(debateList[40]);
week10List.Add(debateList[41]);
week10List.Add(debateList[42]);
week10List.Add(debateList[43]);
week10List.Add(debateList[44]);
}
public void finNames()
{
StreamWriter sw = new StreamWriter("nameList.txt");
sw.WriteLine(this.Name1.Text);
sw.WriteLine(this.Name2.Text);
sw.WriteLine(this.Name3.Text);
sw.WriteLine(this.Name4.Text);
sw.WriteLine(this.Name5.Text);
sw.WriteLine(this.Name6.Text);
sw.WriteLine(this.Name7.Text);
sw.WriteLine(this.Name8.Text);
sw.WriteLine(this.Name9.Text);
sw.WriteLine(this.Name10.Text);
sw.Close();
}
public void finWin()
{
StreamWriter sw = new StreamWriter("Win.txt");
sw.WriteLine(this.Win1.Text);
sw.WriteLine(this.Win2.Text);
sw.WriteLine(this.Win3.Text);
sw.WriteLine(this.Win4.Text);
sw.WriteLine(this.Win5.Text);
sw.WriteLine(this.Win6.Text);
sw.WriteLine(this.Win7.Text);
sw.WriteLine(this.Win8.Text);
sw.WriteLine(this.Win9.Text);
sw.WriteLine(this.Win10.Text);
sw.Close();
}
public void finLoss()
{
StreamWriter sw = new StreamWriter("Loss.txt");
sw.WriteLine(this.Loss1.Text);
sw.WriteLine(this.Loss2.Text);
sw.WriteLine(this.Loss3.Text);
sw.WriteLine(this.Loss4.Text);
sw.WriteLine(this.Loss5.Text);
sw.WriteLine(this.Loss6.Text);
sw.WriteLine(this.Loss7.Text);
sw.WriteLine(this.Loss8.Text);
sw.WriteLine(this.Loss9.Text);
sw.WriteLine(this.Loss10.Text);
sw.Close();
}
public void finDebates()
{
StreamWriter sw = new StreamWriter("Debate.txt");
for (int i = 0; i < 45; i++)
{
sw.WriteLine(debateList[i]);
}
sw.Close();
}
private void Save_Click(object sender, EventArgs e)
{
finNames();
finWin();
finLoss();
finDebates();
}
private void Close_Click(object sender, EventArgs e)
{
this.Close();
}
private void ChangeDates_Click(object sender, EventArgs e)
{
ChangeDates form = new ChangeDates(dateList);
form.ShowDialog();
initDates();
ListDates();
}
private void Date1_Click(object sender, EventArgs e)
{
Week_1 form = new Week_1(nameList, week1List);
form.ShowDialog();
}
private void Date2_Click(object sender, EventArgs e)
{
Week_2 form = new Week_2(nameList, week2List);
form.ShowDialog();
}
}
}

Assuming the debateList is the list you'd like to filter:
public List<string> debateList = new List<string>();
You can use Linq's GroupBy method chained with the SelectMany method and take advantage of the Take method which will take up to the max specified even if less are present:
List<string> result = debateList.GroupBy( x => x )
.SelectMany( x => x.Take( 2 ) )
.OrderBy( x => x )
.ToList()

You could set a counting variable, loop through the container and increment the counter each time an element is present in the container.

Related

Returning objects back to a form

I'm a bit new to C# and I'm having a bit of trouble with this code. I apologize if the answer is a trivial thing that I'm missing.
In brief, I'm making something similar to an rpg. The main form has a "start" button. When that button is pressed, some information is read from a text file and that information is used to set up the characters. I have all of the logic I need working in the setup class, but I can't for the life of me figure out how to return the final setup object array back to the form. The form will need the information in those objects back to setup some picture boxes and labels.
Here is the code from the "start" button click event:
private void button1_Click(object sender, EventArgs e)
{
// setup players
Player[] player = new Player[5];
SetupPlayers setupPlayers = new SetupPlayers();
setupPlayers.getPlayerData();
}
and from the Player classes:
public class Player
{
public string name { get; set; }
public string mood { get; set; }
public string icon { get; set; }
public string color { get; set; }
public bool participate { get; set; }
}
/// <summary>
/// Gets player info from setup.txt file, sets up number of players, ID of each player, and calculates mood
/// </summary>
public class SetupPlayers
{
public SetupPlayers()
{
}
public void getPlayerData()
{
int totalPossiblePlayers = 5;
int numberOfPlayers = 0;
String[] playerName = new string[12];
String[] attr1 = new string[12]; String[] attr2 = new string[12]; String[] attr3 = new string[12]; String[] attr4 = new string[12]; String[] attr5 = new string[12]; String[] attr6 = new string[12]; String[] attr7 = new string[12]; String[] attr8 = new string[12];
String[] color = new string[12]; String[] iconP = new string[12];
StreamReader SR = new StreamReader("setup.txt");
for (int counter = 0; counter < 5; counter++)
{
if (SR.Peek() != -1)
{
var splitLine = SR.ReadLine().Split(',');
playerName[counter] = splitLine[0];
attr1[counter] = splitLine[1]; attr2[counter] = splitLine[2]; attr3[counter] = splitLine[3]; attr4[counter] = splitLine[4]; attr5[counter] = splitLine[5]; attr6[counter] = splitLine[6]; attr7[counter] = splitLine[7]; attr8[counter] = splitLine[8];
color[counter] = splitLine[9]; iconP[counter] = splitLine[10];
}
}
SR.Close();
Player[] player = new Player[totalPossiblePlayers];
for (int i = 0; i < totalPossiblePlayers; i++)
{
player[i] = new Player();
player[i].name = playerName[i];
player[i].icon = iconP[i];
player[i].color = color[i];
player[i].mood = setPlayerMood(i, attr1, attr2, attr3, attr4, attr5, attr6, attr7, attr8);
player[i].participate = doesPlayerJoin();
}
}
//setPlayerMood and doesPlayerJoin code below removed, they are working great
}
So I need all this player[] object array to be accessible to the form.
Thanks for reading!
Ken
You need to return players and assign it in button click event.
I have made sme modifications to setup players like having using statement for streamreader & simplified streamReader.
private void button1_Click(object sender, EventArgs e)
{
// setup players
SetupPlayers setupPlayers = new SetupPlayers();
Player[] players = setupPlayers.getPlayerData();
}
public Player[] getPlayerData()
{
var totalPossiblePlayers = 5;
var numberOfPlayers = 0;
var playerName = new string[12];
var attr1 = new string[12];
var attr2 = new string[12];
var attr3 = new string[12];
var attr4 = new string[12];
var attr5 = new string[12];
var attr6 = new string[12];
var attr7 = new string[12];
var attr8 = new string[12];
var color = new string[12];
var iconP = new string[12];
using var sr = System.IO.File.OpenText("setup.txt");
for (var counter = 0; counter < 5; counter++)
{
if (sr.Peek() != -1)
{
var splitLine = sr.ReadLine()?.Split(',');
if (splitLine != null)
{
playerName[counter] = splitLine[0];
attr1[counter] = splitLine[1];
attr2[counter] = splitLine[2];
attr3[counter] = splitLine[3];
attr4[counter] = splitLine[4];
attr5[counter] = splitLine[5];
attr6[counter] = splitLine[6];
attr7[counter] = splitLine[7];
attr8[counter] = splitLine[8];
color[counter] = splitLine[9];
iconP[counter] = splitLine[10];
}
}
}
Player[] player = new Player[totalPossiblePlayers];
for (var i = 0; i < totalPossiblePlayers; i++)
{
player[i] = new Player();
player[i].name = playerName[i];
player[i].icon = iconP[i];
player[i].color = color[i];
player[i].mood = setPlayerMood(i, attr1, attr2, attr3, attr4, attr5, attr6, attr7, attr8);
player[i].participate = doesPlayerJoin();
}
return player;
}

Shuffling list of competitors, each month different competitor

for my friends sportscompetition, each player has to play 1 game a month against an other player. Now if i have a list of 20 players or so its not that hard to randomize the first month so i have 10 matches.
All the months after that though i'm not sure how to get the randomizer working so they won't be matched against a player they have played against.
Right now i made an sql database with Players(Name, (int)Id, Email) , Matches(Id, Player1ID, Player2ID)
I'm thinking for a randomize of the list and checking if each match doesn't contain 2 id's from a match in the database. And if 1 match does, redo the entire randomize of that month.
But i'm not sure if thats the best way.
This is what i have so far, i have yet to test it after i add some 'leden' and 'matches' to my database.
private void MaakMatchen(Maand maand)
{
List<Lid> leden = new List<Lid>();
var dbManager = new Manager();
using (var conGildenhof = dbManager.GetConnection())
{
using (var comLeden = conGildenhof.CreateCommand())
{
comLeden.CommandType = CommandType.Text;
comLeden.CommandText = "select * from dbo.Leden";
conGildenhof.Open();
using (var alleleden = comLeden.ExecuteReader())
{
Int32 voornaamPos = alleleden.GetOrdinal("Voornaam");
Int32 familienaamPos = alleleden.GetOrdinal("Familienaam");
Int32 LidNummerPos = alleleden.GetOrdinal("LidNummer");
while (alleleden.Read())
{
leden.Add(new Classes.Lid(alleleden.GetString(voornaamPos), alleleden.GetString(familienaamPos), alleleden.GetInt32(LidNummerPos)));
}
leden = Randomize(leden);
}
}
using (var comInsert = conGildenhof.CreateCommand())
{
comInsert.CommandType = CommandType.Text;
comInsert.CommandText = "Insert into dbo.Matches (Lid1Id, Lid2Id, Maand) values (#lid1, #lid2, #maand)";
var parLid1 = comInsert.CreateParameter();
parLid1.ParameterName = "#lid1";
comInsert.Parameters.Add(parLid1);
var parLid2 = comInsert.CreateParameter();
parLid2.ParameterName = "#lid2";
comInsert.Parameters.Add(parLid2);
var parMaand = comInsert.CreateParameter();
parMaand.ParameterName = "#maand";
comInsert.Parameters.Add(parMaand);
int lengte = leden.Count();
for (int i = 0; i < lengte; i = i + 2)
{
parLid1.Value = leden[i].LidNummer;
parLid2.Value = leden[i + 1].LidNummer;
parMaand.Value = (int)maand;
comInsert.ExecuteNonQuery();
}
}
}
}
private List<Lid> Randomize(List<Lid> leden)
{
for (int i=0;i<100;i++)
{
leden = Shuffle(leden);
}
int temp = CheckUp(leden);
while (temp != 100)
{
leden = Shuffle(leden, temp);
temp = CheckUp(leden);
}
return leden;
}
private List<Lid> Shuffle(List<Lid> leden)
{
Random rnd = new Random();
int a = rnd.Next(1, leden.Count() + 1);
int b = rnd.Next(1, leden.Count() + 1);
var temp = new Lid();
temp = leden[a];
leden[a] = leden[b];
leden[b] = temp;
return leden;
}
private List<Lid> Shuffle(List<Lid> leden, int id)
{
Random rnd = new Random();
int a = rnd.Next(1, leden.Count() + 1);
int b = id;
var temp = new Lid();
temp = leden[a];
leden[a] = leden[b];
leden[b] = temp;
return leden;
}
private int CheckUp(List<Lid> leden)
{
int lengte = leden.Count();
List<Matches> matches = new List<Matches>();
var dbManager = new Manager();
using (var conGildenhof = dbManager.GetConnection())
{
using (var comMatches = conGildenhof.CreateCommand())
{
comMatches.CommandType = CommandType.Text;
comMatches.CommandText = "select * from dbo.Matches";
conGildenhof.Open();
using (var allematches = comMatches.ExecuteReader())
{
Int32 lid1Pos = allematches.GetOrdinal("Lid1Id");
Int32 lid2Pos = allematches.GetOrdinal("Lid2Id");
Int32 maandPos = allematches.GetOrdinal("Maand");
while (allematches.Read())
{
matches.Add(new Classes.Matches(allematches.GetInt32(lid1Pos), allematches.GetInt32(lid2Pos), (Maand)allematches.GetInt32(maandPos)));
}
}
}
}
for (int i=0;i<lengte;i=i+2)
{
foreach (Matches match in matches)
{
if (leden[i].LidNummer == match.Lid1Id)
{
if (leden[i + 1].LidNummer == match.Lid2Id)
return leden[i].LidNummer;
}
if (leden[i].LidNummer == match.Lid2Id)
{
if (leden[i + 1].LidNummer == match.Lid1Id)
return leden[i].LidNummer;
}
}
}
return 100;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var maand = new int();
int.TryParse(TextBoxMaand.Text, out maand);
if (maand == 0)
TextBoxMaand.Text = "GETAL!";
else
{
MaakMatchen((Maand)maand);
}
}
I would shuffle the players first and create the matches in a second iteration
List<Player> players = GetPlayers();
Random _rnd = new Random();
// shuffle players
players = players.OrderBy(_ => _rnd.Next()).ToList();
// create matches
var matches = players.Take(players.Count / 2).Zip(players.Skip(players.Count / 2), (p1, p2) => new Match(p1,p2));
https://dotnetfiddle.net/sGxbx4

Is that logic to reset the Lists and call Init() over again if there was an exception with the download/s?

And how can i report to the user that there was a problem and that it's trying over again ? And should i just do it like i'm doing it now reseting everything and calling Init() again or should i use some timer and wait some seconds before trying again ?
In the class i did:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Xml;
using HtmlAgilityPack;
using System.ComponentModel;
namespace TestingDownloads
{
class ExtractImages
{
static WebClient client;
static string htmltoextract;
public static List<string> countriescodes = new List<string>();
public static List<string> countriesnames = new List<string>();
public static List<string> DatesAndTimes = new List<string>();
public static List<string> imagesUrls = new List<string>();
static string firstUrlPart = "http://www.sat24.com/image2.ashx?region=";
static string secondUrlPart = "&time=";
static string thirdUrlPart = "&ir=";
public class ProgressEventArgs : EventArgs
{
public int Percentage { get; set; }
public string StateText { get; set; }
}
public event EventHandler<ProgressEventArgs> ProgressChanged;
public void Init()
{
object obj = null;
int index = 0;
ExtractCountires();
foreach (string cc in countriescodes)
{
// raise event here
ProgressChanged?.Invoke(obj,new ProgressEventArgs{ Percentage = 100 * index / countriescodes.Count, StateText = cc });
ExtractDateAndTime("http://www.sat24.com/image2.ashx?region=" + cc);
index +=1;
}
ImagesLinks();
}
public void ExtractCountires()
{
try
{
htmltoextract = "http://sat24.com/en/?ir=true";//"http://sat24.com/en/";// + regions;
client = new WebClient();
client.DownloadFile(htmltoextract, #"c:\temp\sat24.html");
client.Dispose();
string tag1 = "<li><a href=\"/en/";
string tag2 = "</a></li>";
string s = System.IO.File.ReadAllText(#"c:\temp\sat24.html");
s = s.Substring(s.IndexOf(tag1));
s = s.Substring(0, s.LastIndexOf(tag2) + tag2.ToCharArray().Length);
s = s.Replace("\r", "").Replace("\n", "").Replace(" ", "");
string[] parts = s.Split(new string[] { tag1, tag2 }, StringSplitOptions.RemoveEmptyEntries);
string tag3 = "<li><ahref=\"/en/";
for (int i = 0; i < parts.Length; i++)
{
if (i == 17)
{
//break;
}
string l = "";
if (parts[i].Contains(tag3))
l = parts[i].Replace(tag3, "");
string z1 = l.Substring(0, l.IndexOf('"'));
if (z1.Contains("</ul></li><liclass="))
{
z1 = z1.Replace("</ul></li><liclass=", "af");
}
countriescodes.Add(z1);
countriescodes.GroupBy(n => n).Any(c => c.Count() > 1);
string z2 = parts[i].Substring(parts[i].LastIndexOf('>') + 1);
if (z2.Contains("&"))
{
z2 = z2.Replace("&", " & ");
}
countriesnames.Add(z2);
countriesnames.GroupBy(n => n).Any(c => c.Count() > 1);
}
}
catch (Exception e)
{
if (countriescodes.Count == 0)
{
countriescodes = new List<string>();
countriesnames = new List<string>();
DatesAndTimes = new List<string>();
imagesUrls = new List<string>();
Init();
}
}
}
public void ExtractDateAndTime(string baseAddress)
{
try
{
var wc = new WebClient();
wc.BaseAddress = baseAddress;
HtmlDocument doc = new HtmlDocument();
var temp = wc.DownloadData("/en");
doc.Load(new MemoryStream(temp));
var secTokenScript = doc.DocumentNode.Descendants()
.Where(e =>
String.Compare(e.Name, "script", true) == 0 &&
String.Compare(e.ParentNode.Name, "div", true) == 0 &&
e.InnerText.Length > 0 &&
e.InnerText.Trim().StartsWith("var region")
).FirstOrDefault().InnerText;
var securityToken = secTokenScript;
securityToken = securityToken.Substring(0, securityToken.IndexOf("arrayImageTimes.push"));
securityToken = secTokenScript.Substring(securityToken.Length).Replace("arrayImageTimes.push('", "").Replace("')", "");
var dates = securityToken.Trim().Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
var scriptDates = dates.Select(x => new ScriptDate { DateString = x });
foreach (var date in scriptDates)
{
DatesAndTimes.Add(date.DateString);
}
}
catch
{
countriescodes = new List<string>();
countriesnames = new List<string>();
DatesAndTimes = new List<string>();
imagesUrls = new List<string>();
this.Init();
}
}
public class ScriptDate
{
public string DateString { get; set; }
public int Year
{
get
{
return Convert.ToInt32(this.DateString.Substring(0, 4));
}
}
public int Month
{
get
{
return Convert.ToInt32(this.DateString.Substring(4, 2));
}
}
public int Day
{
get
{
return Convert.ToInt32(this.DateString.Substring(6, 2));
}
}
public int Hours
{
get
{
return Convert.ToInt32(this.DateString.Substring(8, 2));
}
}
public int Minutes
{
get
{
return Convert.ToInt32(this.DateString.Substring(10, 2));
}
}
}
public void ImagesLinks()
{
int cnt = 0;
foreach (string countryCode in countriescodes)
{
cnt++;
for (; cnt < DatesAndTimes.Count(); cnt++)
{
string imageUrl = firstUrlPart + countryCode + secondUrlPart + DatesAndTimes[cnt] + thirdUrlPart + "true";
imagesUrls.Add(imageUrl);
if (cnt % 10 == 0) break;
}
}
}
}
}
In both cases where i'm using try and catch if it's getting to the catch i'm trying over again the whole operation by reseting the Lists and calling Init() again.
Then in form1
ExtractImages ei = new ExtractImages();
public Form1()
{
InitializeComponent();
ProgressBar1.Minimum = 0;
ProgressBar1.Maximum = 100;
backgroundWorker1.RunWorkerAsync();
}
events of backgroundworker
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true;
return; // this will fall to the finally and close everything
}
else
{
ei.ProgressChanged += (senders, eee) => backgroundWorker1.ReportProgress(eee.Percentage, eee.StateText);
ei.Init();
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressBar1.Value = e.ProgressPercentage;
label7.Text = e.UserState.ToString();
label8.Text = e.ProgressPercentage + "%";
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
ProgressBar1.Value = 100;
}
else
{
}
}
Another thing not sure if it's a problem. When there is no a problem with the class i see in form1 in label7 all the codes running from the first to the last.
But the progressBar1.Value and label8 both are getting only to 97% so i need in the completed event to add the progressBar1.Value = 100; is that fine or there is a problem with the reporting calculation in the class ?
For the 1st question:
Better catch the exception at client side and display the error msg, 'cause you probably need to control your program behavior accrodingly when sth went wrong.
Consider this: if the DOM struct of the page changed, according to you code, your program probably throw expections, with each exception catching, client.DownloadFile is excuted for one time, if this happens, u will need to know what go wrong, and if u donot change ur code behavior, such hi-freq client.DownloadFile excution will cause the firewall of the website block ur ip for a while.
Add a timer at client side is a good idea i think.
For the 2nd one:
did u define how to handle RunWorkerCompleted event?

Get the positions of unique elements in a string[]

I have an xml file that I am accessing to create a report of time spent on a project. I'm returning the unique dates to a label created dynamically on a winform and would like to compile the time spent on a project for each unique date. I have been able to return all of the projects under each date or only one project. Currently I'm stuck on only returning one project. Can anyone please help me?? This is what the data should look like if it's correct.
04/11/15
26820 2.25
27111 8.00
04/12/15
26820 8.00
04/13/15
01det 4.33
26820 1.33
27225 4.25
etc.
This is how I'm retrieving the data
string[] weekDateString = elementDateWeekstring();
string[] uniqueDates = null;
string[] weeklyJobNumber = elementJobNumWeek();
string[] weeklyTicks = elementTicksWeek();
This is how I'm getting the unique dates.
IEnumerable<string> distinctWeekDateIE = weekDateString.Distinct();
foreach (string d in distinctWeekDateIE)
{
uniqueDates = distinctWeekDateIE.ToArray();
}
And this is how I'm creating the labels.
try
{
int dateCount;
dateCount = uniqueDates.Length;
Label[] lblDate = new Label[dateCount];
int htDate = 1;
int padDate = 10;
for (int i = 0; i < dateCount; i++ )
{
lblDate[i] = new Label();
lblDate[i].Name = uniqueDates[i].Trim('\r');
lblDate[i].Text = uniqueDates[i];
lblDate[i].TabIndex = i;
lblDate[i].Bounds = new Rectangle(18, 275 + padDate + htDate, 75, 22);
targetForm.Controls.Add(lblDate[i]);
htDate += 22;
foreach (string x in uniqueDates)
{
int[] posJobNumber;
posJobNumber = weekDateString.Select((b, a) => b == uniqueDates[i].ToString() ? a : -1).Where(a => a != -1).ToArray();
for (int pjn = 0; pjn < posJobNumber.Length; pjn++)
{
if (x.Equals(lblDate[i].Text))
{
Label lblJobNum = new Label();
int htJobNum = 1;
int padJobNum = 10;
lblJobNum.Name = weeklyJobNumber[i];
lblJobNum.Text = weeklyJobNumber[i];
lblJobNum.Bounds = new Rectangle(100, 295 + padJobNum + htJobNum, 75, 22);
targetForm.Controls.Add(lblJobNum);
htJobNum += 22;
htDate += 22;
padJobNum += 22;
}
}
}
}
}
I've been stuck on this for about 3 months. Is there anyone that can describe to me why I'm not able to properly retrieve the job numbers that are associated with a particular date. I don't believe that these are specifically being returned as dates. Just a string that looks like a date.
I really appreciate any help I can get. I'm just completely baffled. Thank you for any responses in advance. I truly appreciate the assistance.
EDIT: #Sayka - Here is the xml sample.
<?xml version="1.0" encoding="utf-8"?>
<Form1>
<Name Key="4/21/2014 6:51:17 AM">
<Date>4/21/2014</Date>
<JobNum>26820</JobNum>
<RevNum>00000</RevNum>
<Task>Modeling Secondary</Task>
<Start>06:51 AM</Start>
<End>04:27 PM</End>
<TotalTime>345945089017</TotalTime>
</Name>
<Name Key="4/22/2014 5:44:22 AM">
<Date>4/22/2014</Date>
<JobNum>26820</JobNum>
<RevNum>00000</RevNum>
<Task>Modeling Secondary</Task>
<Start>05:44 AM</Start>
<End>06:56 AM</End>
<TotalTime>43514201221</TotalTime>
</Name>
<Name Key="4/22/2014 6:57:02 AM">
<Date>4/22/2014</Date>
<JobNum>02e-n-g</JobNum>
<RevNum>00000</RevNum>
<Task>NET Eng</Task>
<Start>06:57 AM</Start>
<End>07:16 AM</End>
<TotalTime>11706118875</TotalTime>
</Name>
....
</Form1>
This is how I'm getting the information out of the xml file and returning a string[].
public static string[] elementDateWeekstring()
{
//string datetxtWeek = "";
XmlDocument xmldoc = new XmlDocument();
fileExistsWeek(xmldoc);
XmlNodeList nodeDate = xmldoc.GetElementsByTagName("Date");
int countTicks = 0;
string[] dateTxtWeek = new string[nodeDate.Count];
for (int i = 0; i < nodeDate.Count; i++)
{
dateTxtWeek[i] = nodeDate[i].InnerText;
countTicks++;
}
return dateTxtWeek;
}
Job number and Ticks are returned in a similar fashion. I've been able to reuse these snippets throught out the code. This is a one dimensional xml file?? It will always return a position for a jobnumber that equates to a date or Ticks. I will never have more or less of any one element.
You can use Linq-to-XML to parse the XML file, and then use Linq-to-objects to group (and order) the data by job date and order each group by job name.
The code to parse the XML file is like so:
var doc = XDocument.Load(filename);
var jobs = doc.Descendants("Name");
// Extract the date, job number, and total time from each "Name" element.:
var data = jobs.Select(job => new
{
Date = (DateTime)job.Element("Date"),
Number = (string)job.Element("JobNum"),
Duration = TimeSpan.FromTicks((long)job.Element("TotalTime"))
});
The code to group and order the jobs by date and order the groups by job name is:
var result =
data.GroupBy(job => job.Date).OrderBy(g => g.Key)
.Select(g => new
{
Date = g.Key,
Jobs = g.OrderBy(item => item.Number)
});
Then you can access the data by iterating over each group in result and then iterate over each job in the group, like so:
foreach (var jobsOnDate in result)
{
Console.WriteLine("{0:d}", jobsOnDate.Date);
foreach (var job in jobsOnDate.Jobs)
Console.WriteLine(" {0} {1:hh\\:mm}", job.Number, job.Duration);
}
Putting this all together in a sample compilable console application (substitute the filename for the XML file as appropriate):
using System;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
private static void Main()
{
string filename = #"d:\test\test.xml"; // Substitute your own filename here.
// Open XML file and get a collection of each "Name" element.
var doc = XDocument.Load(filename);
var jobs = doc.Descendants("Name");
// Extract the date, job number, and total time from each "Name" element.:
var data = jobs.Select(job => new
{
Date = (DateTime)job.Element("Date"),
Number = (string)job.Element("JobNum"),
Duration = TimeSpan.FromTicks((long)job.Element("TotalTime"))
});
// Group the jobs by date, and order the groups by job name:
var result =
data.GroupBy(job => job.Date).OrderBy(g => g.Key)
.Select(g => new
{
Date = g.Key,
Jobs = g.OrderBy(item => item.Number)
});
// Print out the results:
foreach (var jobsOnDate in result)
{
Console.WriteLine("{0:d}", jobsOnDate.Date);
foreach (var job in jobsOnDate.Jobs)
Console.WriteLine(" {0} {1:hh\\:mm}", job.Number, job.Duration);
}
}
}
}
The output is like this
Create a new project
Set form size bigger.
Apply these codes.
Set the location for your XML file.
Namespaces
using System.Xml;
using System.IO;
Form Code
public partial class Form1 : Form
{
const string XML_FILE_NAME = "D:\\emps.txt";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
prepareDataGrid();
List<JOBS> jobsList = prepareXML(XML_FILE_NAME);
for (int i = 0; i < jobsList.Count; i++)
{
addDateRow(jobsList[i].jobDate.ToString("M'/'d'/'yyyy"));
for (int j = 0; j < jobsList[i].jobDetailsList.Count; j++)
dgv.Rows.Add(new string[] {
jobsList[i].jobDetailsList[j].JobNumber,
jobsList[i].jobDetailsList[j].JobHours
});
}
}
DataGridView dgv;
void prepareDataGrid()
{
dgv = new DataGridView();
dgv.BackgroundColor = Color.White;
dgv.GridColor = Color.White;
dgv.DefaultCellStyle.SelectionBackColor = Color.White;
dgv.DefaultCellStyle.SelectionForeColor = Color.Black;
dgv.DefaultCellStyle.ForeColor = Color.Black;
dgv.DefaultCellStyle.BackColor = Color.White;
dgv.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
dgv.Width = 600;
dgv.Dock = DockStyle.Left;
this.BackColor = Color.White;
dgv.Columns.Add("Col1", "Col1");
dgv.Columns.Add("Col2", "Col2");
dgv.Columns[0].Width = 110;
dgv.Columns[1].Width = 40;
dgv.DefaultCellStyle.Font = new System.Drawing.Font("Segoe UI", 10);
dgv.RowHeadersVisible = dgv.ColumnHeadersVisible = false;
dgv.AllowUserToAddRows =
dgv.AllowUserToDeleteRows =
dgv.AllowUserToOrderColumns =
dgv.AllowUserToResizeColumns =
dgv.AllowUserToResizeRows =
!(dgv.ReadOnly = true);
Controls.Add(dgv);
}
void addJobRow(string jobNum, string jobHours)
{
dgv.Rows.Add(new string[] {jobNum, jobHours });
}
void addDateRow(string date)
{
dgv.Rows.Add(new string[] { date, ""});
dgv.Rows[dgv.Rows.Count - 1].DefaultCellStyle.SelectionForeColor =
dgv.Rows[dgv.Rows.Count - 1].DefaultCellStyle.ForeColor = Color.Firebrick;
dgv.Rows[dgv.Rows.Count - 1].DefaultCellStyle.Font = new Font("Segoe UI Light", 13.5F);
dgv.Rows[dgv.Rows.Count - 1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;
dgv.Rows[dgv.Rows.Count - 1].Height = 25;
}
List<JOBS> prepareXML(string fileName)
{
string xmlContent = "";
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
using (StreamReader sr = new StreamReader(fs)) xmlContent = sr.ReadToEnd();
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlContent);
List<JOBS> jobsList = new List<JOBS>();
XmlNode form1Node = doc.ChildNodes[1];
for (int i = 0; i < form1Node.ChildNodes.Count; i++)
{
XmlNode dateNode = form1Node.ChildNodes[i].ChildNodes[0].ChildNodes[0],
jobNumNode = form1Node.ChildNodes[i].ChildNodes[1].ChildNodes[0],
timeTicksNode = form1Node.ChildNodes[i].ChildNodes[6].ChildNodes[0];
bool foundDate = false;
for (int j = 0; j < jobsList.Count; j++) if (jobsList[j].compareDate(dateNode.Value))
{
jobsList[j].addJob(jobNumNode.Value, Math.Round(TimeSpan.FromTicks(
(long)Convert.ToDouble(timeTicksNode.Value)).TotalHours, 2).ToString());
foundDate = true;
break;
}
if (!foundDate)
{
JOBS job = new JOBS(dateNode.Value);
string jbnum = jobNumNode.Value;
string tbtck = timeTicksNode.Value;
long tktk = Convert.ToInt64(tbtck);
double tkdb = TimeSpan.FromTicks(tktk).TotalHours;
job.addJob(jobNumNode.Value, Math.Round(TimeSpan.FromTicks(
Convert.ToInt64(timeTicksNode.Value)).TotalHours, 2).ToString());
jobsList.Add(job);
}
}
jobsList.OrderByDescending(x => x.jobDate);
return jobsList;
}
class JOBS
{
public DateTime jobDate;
public List<JobDetails> jobDetailsList = new List<JobDetails>();
public void addJob(string jobNumber, string jobHours)
{
jobDetailsList.Add(new JobDetails() { JobHours = jobHours, JobNumber = jobNumber });
}
public JOBS(string dateString)
{
jobDate = getDateFromString(dateString);
}
public JOBS() { }
public bool compareDate(string dateString)
{
return getDateFromString(dateString) == jobDate;
}
private DateTime getDateFromString(string dateString)
{
string[] vals = dateString.Split('/');
return new DateTime(Convert.ToInt32(vals[2]), Convert.ToInt32(vals[0]), Convert.ToInt32(vals[1]));
}
}
class JobDetails
{
public string JobNumber { get; set; }
public string JobHours { get; set; }
}
}

Performance problems with Linq To CSV

I have found a library which connects csv-files with linq. I understood the principles and my code works well. But i have some problems with big csv files.
http://www.codeproject.com/Articles/25133/LINQ-to-CSV-library
Now i want to access specific single items from my _dataTable object. I get them like this:
public class CsvFile
{
private IEnumerable<DataRow> _dataTable = cc.Read<DataRow>(_filePath, _inputFileDescription);
public string GetItem(int row, int column)
{
return _dataTable.ElementAt<DataRow>(row).ElementAt<DataRowItem>(column).Value;
}
}
When i now call the method like this in a loop:
CsvFile file1 = new CsvFile("C:\\dev_csvcompare\\Master.csv", ';', true);
for(int i = 0; i < 10000; i++)
{
string dummy = file1.GetItem(1, i); //Does not make sense, my loop is a bit more complicated
}
it gets very slow, because the IEnumerable opens the stream every call.
In the documentation(link) under "Deferred Reading" they say i can access the ienumerable "_dataTable" with a foreach loop (this does work fine), but this is in my case no option because i want access to specific items in the csv.
Are there possibilities to keep the filestream open so that the performace increases?
EDIT (My code, maybe a lot of nosense, im not so experienced with .net, c# and oop):
public void Compare(int key1, int key2, int col1, int col2)
{
string lastKeyCol1 = null;
string lastKeyCol2 = null;
List<string> sortedKeyColFile1 = new List<string>();
List<string> sortedKeyColFile2 = new List<string>();
int file1counter = 0;
int file2counter = 0;
int cnt = 0;
sortedKeyColFile1 = _file1.GetCol(key1);
sortedKeyColFile1.Sort();
sortedKeyColFile2 = _file2.GetCol(key2);
sortedKeyColFile2.Sort();
while ((file1counter < sortedKeyColFile1.Count) || (file2counter < sortedKeyColFile2.Count))
{
_outputList.Add(new OutputValues(key1, key2, col1, col2));
//Keys are in both files
if (sortedKeyColFile1[file1counter] == sortedKeyColFile2[file2counter])
{
if (lastKeyCol1 == sortedKeyColFile1[file1counter])
{
//Keys are redundant
_outputList[cnt].RedundantKeyF1 = true;
}
if (lastKeyCol2 == sortedKeyColFile2[file2counter])
{
//Keys are redundant
_outputList[cnt].RedundantKeyF2 = true;
}
lastKeyCol1 = sortedKeyColFile1[file1counter];
lastKeyCol2 = sortedKeyColFile2[file2counter];
_outputList[cnt].ValF1 = _file1.GetItem(file1counter, col1);
_outputList[cnt].ValF2 = _file2.GetItem(file2counter, col2);
_outputList[cnt].LineNumF1 = file1counter;
_outputList[cnt].LineNumF2 = file2counter;
//compare the values (because keys do match at this place)
_outputList[cnt].CompareResult = CompareString(_file1.GetItem(file1counter, col1), _file2.GetItem(file2counter, col2));
if (file1counter < sortedKeyColFile1.Count)
{
file1counter++;
}
if (file2counter < sortedKeyColFile2.Count)
{
file2counter++;
}
}
//Key sortedKeyColFile2[file2counter] is not in file 1
else if (file2counter < sortedKeyColFile2.Count && 0 < (string.Compare(sortedKeyColFile1[file1counter], sortedKeyColFile2[file2counter])))
{
_outputList[cnt].LineNumF2 = file2counter;
if (lastKeyCol2 == sortedKeyColFile2[file2counter])
{
//Keys are redundant
_outputList[cnt].RedundantKeyF2 = true;
}
lastKeyCol2 = sortedKeyColFile2[file2counter];
file2counter++;
}
//Key sortedKeyColFile1[file1counter] is not in file 2
else if (file1counter < sortedKeyColFile1.Count)
{
_outputList[cnt].LineNumF1 = file1counter;
if (lastKeyCol1 == sortedKeyColFile1[file1counter])
{
//Keys are redundant
_outputList[cnt].RedundantKeyF1 = true;
}
lastKeyCol1 = sortedKeyColFile1[file1counter];
file1counter++;
}
cnt++;
}
}
//And here the important part of the csv-file class, maybe not so interesting
public class CsvFile
{
private string _filePath = null;
private char _separator = ',';
private bool _hasHeader = true;
private CsvContext _cc = null;
private CsvFileDescription _inputFileDescription = null;
private List<string> _headers = null;
private IEnumerable<DataRow> _dataTable = null;
/// <summary>
/// Constructor for a new CsvFile object.
/// The Constructor initiates the Object and read the values out of the File
/// </summary>
/// <param name="filePath">Full path of the csv-file</param>
/// <param name="separator">Seperator of the csv-file, eg: ';' or ',' or '\t'</param>
/// <param name="hasHeader">Is true if the first col of the csv-file contains a headers</param>
public CsvFile(string filePath, char separator, bool hasHeader = true)
{
//Throws an exception if something is wrong with the file
File.OpenRead(filePath);
_filePath = filePath;
_separator = separator;
_hasHeader = hasHeader;
_cc = new CsvContext();
_inputFileDescription = new CsvFileDescription
{
SeparatorChar = separator,
FirstLineHasColumnNames = hasHeader
};
_dataTable = _cc.Read<DataRow>(_filePath, _inputFileDescription);
if (hasHeader)
{
ParseHeaders();
}
}
public List<string> GetCol(int col)
{
List<string> column = new List<string>();
int cnt = 0;
foreach(DataRow x in _dataTable)
{
column.Add(x[col].Value);
cnt++;
}
return column;
}
private void ParseHeaders()
{
System.IO.StreamReader file = new System.IO.StreamReader(_filePath);
if (!file.EndOfStream)
{
//_headers = file.ReadLine().Split(_separator);
_headers = new List<string> (file.ReadLine().Split(_separator));
}
file.Close();
}
}
Try this:
public class CsvFile
{
private IEnumerable<DataRow> rows = cc.Read<DataRow>(_filePath, _inputFileDescription);
//...
public IEnumerable<DataRow> Rows { get { return rows; } }
}
And then:
CsvFile file1 = new CsvFile("C:\\dev_csvcompare\\Master.csv", ';', true);
foreach(DataRow row in file1.Rows)
{
string dummy = row[1];
}

Categories