Custom Queue Class Iteration and Data Retrieval C# - c#

So I'm working in Visual Studio 2015 with a few custom classes. One of which is called MinPriorityQueue, and it is a priority queue that, in this situation, allows me to retrieve the object of MinimumPriority in the queue via a property MinimumPriority. There is also a method called RemoveMinimumPriority, which is self-explanatory.
I am not allowed to modify this method, it was pre-made for us for this assignment, otherwise I would have already found a simple solution.
My program is meant to compare two text files, and return a value based off a certain equation which isn't important as far as this post goes. The problem I am having is within my UserInterface code. Here is my click event for the 'Analyze' button on my GUI.
private void uxAnalyze_Click(object sender, EventArgs e)
{
Dictionary<string, StoreWord> dictionary = new Dictionary<string, StoreWord>();
const int _numFiles = 2;
MinPriorityQueue<float, StoreInfo> minQueue = new MinPriorityQueue<float, StoreInfo>();
int numWords1 = 0;
int numWords2 = 0;
//Process Both Input Files
using (StreamReader sr = new StreamReader(uxTextBox1.Text))
{
for (int i = 0; i < _numFiles; i++)
{
if (i == 0)
{
dictionary = ReadFile(dictionary, uxTextBox1.Text, i, out numWords1);
}
if (i == 1)
{
dictionary = ReadFile(dictionary, uxTextBox2.Text, i, out numWords2);
}
}
}
int[] numWords = new int[2];
numWords[0] = numWords1;
numWords[1] = numWords2;
//Get 50 Words with Highest Combined Frequencies
foreach(var entry in dictionary.Values)
{
StoreInfo freq = new StoreInfo(entry, numWords);
minQueue.Add(freq, Convert.ToSingle(entry[0] + entry[1]));
if(minQueue.Count > 50)
{
minQueue.RemoveMinimumPriority();
}
}
//Compute and Display the Difference Measure
float diffMeasure = 0;
float temp = 0;
foreach( x in minQueue)
for (int i = 0; i < minQueue.Count; i++)
{
temp += minQueue.????; //This is where my problem stems
}
diffMeasure = (float)(100 * Math.Sqrt(temp));
}
A few lines from the end you will see a comment showing where my problem is located. The MinPriorityQueue (minQueue) has two parameters, a Priority, and a Value, where the Priority is a Float, and the Value is another class called StoreInfo. This class has an Indexer, which will return information from a different file depending on what the index is. In this case, there are only two files. For example: StoreInfo[i] returns the frequency of a word in the ith text file.
Ideally, my code would look like this:
for (int i = 0; i < minQueue.Count; i++)
{
temp += (minQueue.minimumValue[0] - minQueue.minimumValue[1])*(minQueue.minimumValue[0] - minQueue.minimumValue[1]);
}
diffMeasure = (float)(100 * Math.Sqrt(temp));
Problem is, that would require a minimumValue property, which I don't have access to. All I have is minimumPriority.
As far as I can see, there is no other way for me to get the Values that I need in order to get the frequencies that I need to get from the indexer and put into the equation.
Help is much appreciated.

Alright guys, I've been thinking at this for far too long, and it doesn't seem like anyone else sees another solution either.
At this point, I'm just going to go with the logical solution and add another property into the MinPriorityQueue class, even though it is against my professor's wishes.
Thank you all anyway.

Related

How to make this code more functional or 'prettier'

I've been working on a project where I need on a button press that this line gets executed.
if (listView1.SelectedItems[0].SubItems[3].Text == "0") //Checks to see Value
{
listView1.SelectedItems[0].SubItems[3].Text = "1";// If Value is Greater, Increase and Change ListView
questionNumberLabel.Text = listView1.SelectedItems[0].SubItems[3].Text;// Increase and Change Label
}
Now I have this repeated about 10 times with each value increasing by one. But I know that this is ugly, and dysfunctional. As well as conflates the file size. I've tried a few things. Primarily this method.
if (listView1.SelectedItems[0].SubItems[3].Text == "0")
{
for (var i = 1; i < 100;)
{
if (!Int32.TryParse(listView1.SelectedItems[0].SubItems[3].Text, out i))
{
i = 0;
}
i++;
listView1.SelectedItems[0].SubItems[3].Text = i.ToString();
Console.WriteLine(i);
}
}
But instead of just adding one, it does the 100 instances and ends. The reason this is becoming a pain in the *** is because the
listView1.SelectedItems[0].SubItems[3].Text
is just that - it's a string, not an int. That's why I parsed it and tried to run it like that. But it still isn't having the out come I want.
I've also tried this
string listViewItemToChange = listView1.SelectedItems[0].SubItems[3].Text;
Then parsing the string, to make it prettier. It worked like it did before, but still hasn't given me the outcome I want. Which to reiterate is, I'm wanting the String taken from the list view to be changed into an int, used in the for loop, add 1, then restring it and output it on my listView.
Please help :(
You say you want the text from a listview subitem converted to an int which is then used in a loop
so - first your creating your loop variable, i, then in your loop you're assigning to it potentially 3 different values 2 of which are negated by the, i++. None of it makes sense and you shouldn't be manipulating your loop variable like that (unless understand what you're doing).
if you move statements around a little..
int itemsToCheck = 10; // "Now I have this repeated about 10 times "
for (var item = 0; item < itemsToCheck; item++)
{
int i;
if (!Int32.TryParse(listView1.SelectedItems[item].SubItems[3].Text, out i))
{
i = 0;
}
i++;
listView1.SelectedItems[item].SubItems[3].Text = i.ToString();
Console.WriteLine(i);
}
Something along those lines is what you're looking for. I haven't changed what your code does with i, just added a loop count itemsToCheck and used a different loop variable so your loop variable and parsed value are not one in the same which will likely be buggy.
Maybe this give you an idea. You can start using this syntax from C# 7.0
var s = listView1.SelectedItems[0].SubItems[3].Text;
var isNumeric = int.TryParse(s, out int n);
if(isNumeric is true && n > 0){
questionNumberLabel.Text = s;
}
to shortcut more
var s = listView1.SelectedItems[0].SubItems[3].Text;
if(int.TryParse(s, out int n) && n > 0){
questionNumberLabel.Text = s;
}

Binary search slower, what am I doing wrong?

EDIT: so it looks like this is normal behavior, so can anyone just recommend a faster way to do these numerous intersections?
so my problem is this. I have 8000 lists (strings in each list). For each list (ranging from size 50 to 400), I'm comparing it to every other list and performing a calculation based on the intersection number. So I'll do
list1(intersect)list1= number
list1(intersect)list2= number
list1(intersect)list888= number
And I do this for every list. Previously, I had HashList and my code was essentially this: (well, I was actually searching through properties of an object, so I
had to modify the code a bit, but it's basically this:
I have my two versions below, but if anyone knows anything faster, please let me know!
Loop through AllLists, getting each list, starting with list1, and then do this:
foreach (List list in AllLists)
{
if (list1_length < list_length) //just a check to so I'm looping through the
//smaller list
{
foreach (string word in list1)
{
if (block.generator_list.Contains(word))
{
//simple integer count
}
}
}
// a little more code, but the same, but looping through the other list if it's smaller/bigger
Then I make the lists into regular lists, and applied Sort(), which changed my code to
foreach (List list in AllLists)
{
if (list1_length < list_length) //just a check to so I'm looping through the
//smaller list
{
for (int i = 0; i < list1_length; i++)
{
var test = list.BinarySearch(list1[i]);
if (test > -1)
{
//simple integer count
}
}
}
The first version takes about 6 seconds, the other one takes more than 20 (I just stop there cuz otherwise it would take more than a minute!!!) (and this is for a smallish subset of the data)
I'm sure there's a drastic mistake somewhere, but I can't find it.
Well I have tried three distinct methods for achieving this (assuming I understood the problem correctly). Please note I have used HashSet<int> in order to more easily generate random input.
setting up:
List<HashSet<int>> allSets = new List<HashSet<int>>();
Random rand = new Random();
for(int i = 0; i < 8000; ++i) {
HashSet<int> ints = new HashSet<int>();
for(int j = 0; j < rand.Next(50, 400); ++j) {
ints.Add(rand.Next(0, 1000));
}
allSets.Add(ints);
}
the three methods I checked (code is what runs in the inner loop):
the loop:
note that you are getting duplicated results in your code (intersecting set A with set B and later intersecting set B with set A).
It won't affect your performance thanks to the list length check you are doing. But iterating this way is clearer.
for(int i = 0; i < allSets.Count; ++i) {
for(int j = i + 1; j < allSets.Count; ++j) {
}
}
first method:
used IEnumerable.Intersect() to get the intersection with the other list and checked IEnumerable.Count() to get the size of the intersection.
var intersect = allSets[i].Intersect(allSets[j]);
count = intersect.Count();
this was the slowest one averaging 177s
second method:
cloned the smaller set of the two sets I was intersecting, then used ISet.IntersectWith() and checked the resulting sets Count.
HashSet<int> intersect;
HashSet<int> intersectWith;
if(allSets[i].Count < allSets[j].Count) {
intersect = new HashSet<int>(allSets[i]);
intersectWith = allSets[j];
} else {
intersect = new HashSet<int>(allSets[j]);
intersectWith = allSets[i];
}
intersect.IntersectWith(intersectWith);
count = intersect.Count;
}
}
this one was slightly faster, averaging 154s
third method:
did something very similar to what you did iterated over the shorter set and checked ISet.Contains on the longer set.
for(int i = 0; i < allSets.Count; ++i) {
for(int j = i + 1; j < allSets.Count; ++j) {
count = 0;
if(allSets[i].Count < allSets[j].Count) {
loopingSet = allSets[i];
containsSet = allSets[j];
} else {
loopingSet = allSets[j];
containsSet = allSets[i];
}
foreach(int k in loopingSet) {
if(containsSet.Contains(k)) {
++count;
}
}
}
}
this method was by far the fastest (as expected), averaging 66s
conclusion
the method you're using is the fastest of these three. I certainly can't think of a faster single threaded way to do this. Perhaps there is a better concurrent solution.
I've found that one of the most important considerations in iterating/searching any kind of collection is to choose the collection type very carefully. To iterate through a normal collection for your purposes will not be the most optimal. Try using something like:
System.Collections.Generic.HashSet<T>
Using the Contains() method while iterating over the shorter list of two (as you mentioned you're already doing) should give close to O(1) performance, the same as key lookups in the generic Dictionary type.

Using a string in place of an object when accessing it

First off I'm sure that I am using the wrong terminology here but I will fix it if someone comments on it. Please be gentle.
So I have multiple charts on a page and I am performing virtually identical actions on each. For demonstrative purposes lets call my charts something like: chart1, chart2, ..., chartn where n is somewhere in the vicinity of 20. What I would like to do is drop this in a for loop and perform all the work in one smaller chunk of code, especially if I have to tweak it later.
So my question is whether or not I can vary the n part representing the object (terminology?) so I can get this done more efficiently.
i.e.:
for(int i = 0; i < 20; i++)
{
String chartName = "chart" + i;
chartName.Series.Clear();
}
I have a feeling you can't do this with a string so I was looking into doing a foreach but I don't know how to do this with charts.
Thanks a lot!
You should put the charts in a list. For example, this makes a list of Chart objects (or whatever your chart type is):
List<Chart> charts = new List<Chart>();
Then you can add charts:
charts.Add(new Chart());
And use them:
for (int i = 0; i < charts.Count; i++)
{
charts[i].Series.Clear();
}
Of course, you can make the charts variable a field in your class.
You can directly initialize a list (or array, or dictionary1) like this:
List<Chart> charts = new List<Charts>()
{
new Chart(),
new Chart(),
existingChart1,
existingChart2
};
Or, if you create a new array of objects using that syntax...
Chart[] arrayOfCharts = new []
{
new Chart(),
new Chart(),
existingChart1,
existingChart2
};
...then you can add multiple objects at once using AddRange:
charts.AddRange(arrayOfCharts);
1) You can use this so-called collection initializer syntax on any object that has a public Add method.
Can you access your chart from a list/array/collection of charts like this?
for (int i = 0; i <= 19; i++) {
String chartName = "chart" + i;
Charts(chartName).Series.Clear();
}
or maybe
for (int i = 0; i <= 19; i++) {
String chartName = "chart" + i;
Charts(i).Series.Clear();
}

Dynamic UI, Loop

I created a dynamic UI that shows each module that the user has added to their selection and all the assessments for that module underneath the module name,
All data is saved in array lists - including objects as textBoxes and labels.
Here's what it looks like:
http://www.4shared.com/photo/fullsize/Ja3jHpRJ/screen1.html
Then the user should enter their marks for each assessments
And the Calculate button should calculate their overall mark for each module
At this stage I am just adding the marks together.
The program works fine when there's only one module added, but when there are more modules it seems to think that a TextBox is empty, even though i fill them all with numbers.
here is my code
private void calculateButton_Click(object sender, EventArgs e)
{
int modLength = dat.ModuleTitle.Count;
int modAsses = 0;
int loopCount = 0;
for (int i = 0; i < modLength; i++)
{
int total = 0;
if ((textboxComputer[loopCount] as TextBox).Text == "")
{
Console.WriteLine("!!!!!");
}
else
{
modAsses = int.Parse(dat.ModuleAssessNum[i].ToString());
for (int y = 0; y < modAsses; y++)
{
total = total + int.Parse((textboxComputer[loopCount] as TextBox).Text.ToString());
loopCount = loopCount + 1;
}
Console.WriteLine(total);
}
}
}
When i add two or more modules it displays the "!!!!!" message, but calculates the mark for the first module added.
here's some output
http://www.4shared.com/photo/fullsize/PlY29BMQ/screen2.html
You need to make sure that the code that creates the extra dynamic elements on screen is being called as part of the page load, so that then the viewstate is inserted back into the elements and can be read back out by your code.
there is something wrong with your code, probably a lot wrong(except the names of your variables).
Take a look at your loopCount variable - you will only conditionally change it in your loop but you always use it at the start of the loop(where you do textboxComputer[loopCount]).
So what's the expected behavior of that? The first time you get in to the loop if you go to your 'else' part you may print the total as you say but you also change the loopCount variable, once you get to the Console.WriteLine("!!!!!"); you never change it again - so your iterations will behave the same.

Which collection type to use?

I have a scenario where I have a list of classes, and I want to mix up the order. For example:
private List<Question> myQuestions = new List<Question>();
So, given that this is now populated with a set of data, I want to mix up the order. My first thought was to create a collection of integers numbered from 1 to myQuestions.Count, assign one at random to each question and then loop through them in order; however, I can’t seem to find a suitable collection type to use for this. An example of what I mean would be something like this:
for (int i = 0; i <= myQuestions.Count -1; i++)
tempCollection[i] = myQuestions[rnd.Next(myQuestions.Count-1)];
But I’m not sure what tempCollection should be – it just needs to be a single value that I can remove as I use it. Does anyone have any suggestions as to which type to use, or of a better way to do this?
I suggest you copy the results into a new List<Question> and then shuffle that list.
However, I would use a Fisher-Yates shuffle rather than the one you've given here. There are plenty of C# examples of that on this site.
For example, you might do:
// Don't create a new instance of Random each time. That's a detail
// for another question though.
Random rng = GetAppropriateRandomInstanceForThread();
List<Question> shuffled = new List<Question>(myQuestions);
for (int i = shuffled.Count - 1; i > 0; i--)
{
// Swap element "i" with a random earlier element it (or itself)
int swapIndex = rng.Next(i + 1);
Question tmp = shuffled[i];
shuffled[i] = shuffled[swapIndex];
shuffled[swapIndex] = tmp;
}
You could use Linq and order by a random value:
List<string> items = new List<string>();
items.Add("Foo");
items.Add("Bar");
items.Add("Baz");
foreach (string item in items.OrderBy(c => Guid.NewGuid()))
{
Console.WriteLine(item);
}
temp Collection should be same type as myQuestions.
I would also suggest a change in your code:
for (int i = 0; i <= myQuestions.Count -1; i++)
to
for (int i = 0; i < myQuestions.Count; i++)
Does the same thing, but this is how most programers do it so it will make your code simpler to read.

Categories

Resources