Implementing an intersection merge of 2 array lists - c#

For homework assignment we have to program a Intersection merge of 2 ArrayLists. I have done it using the following code
public void Intersection()
{
foreach (object obj1 in Developed)
{
Apps xApp = (Apps)obj1;
foreach (object obj2 in DPloyed)
{
Apps yApp = (Apps)obj2;
if (xApp.CompareName(yApp) == 0)
{
Inter.Add(yApp);
}
}
}
}
I would like to implement it rather using the while loop but the following code seems to keep missing elements in the list. It puts the first elements in the new intersection list but once the length of developed is increased from 1 element to 5 elements or more it does not add the new elements.
public void Intersection()
{
int i = 0;
int j = 0;
while (i < Developed.Count && j < DPloyed.Count)
{
Apps curA = (Apps)Developed[i];
Apps curB = (Apps)DPloyed[j];
if (curA.CompareName(curB) == 0)
{
Inter.Add(curA);
i++;
j++;
}
else if (curA.CompareName(curB) < 0)
{
i++;
}
else
j++;
}
}
Any help as to why the while loop does not work would be appreciated.
Thanks

Do this
while (i < Developed.Count || j < DPloyed.Count)
because may be both list may be having different Count.
and you need to put extra checks inside loop for indexes so that you don't get Index out of Range Exception.

Problem was not in the actual code for the merges. Problem found in my compare methods.

Related

for loop skip list elements

i have a problem with the code i showed below.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace problem2
{
class Program
{
static void Main(string[] args)
{
int[] nums = Console.ReadLine().Split().Select(int.Parse).ToArray();
List<int> lst = nums.OfType<int>().ToList();
while (true)
{
string des = Console.ReadLine();
var output = Regex.Replace(des.Split(" ")[0], #"[^0-9a-zA-Z\ ]+", "");
if (output == "Add")
{
string val = Regex.Replace(des, "[^0-9]", "");
int value = int.Parse(val);
lst.Add(value);
}
else if(output == "Remove")
{
string val = Regex.Replace(des, "[^0-9]", "");
int value = int.Parse(val);
lst.Remove(value);
}else if(output == "Replace")
{
String result1 = System.Text.RegularExpressions.Regex.Match(des, #"\d+").Value;
var lastno = des.Split().Last();
List<int> lst2 = lst;
for (int i = 0; i < lst2.Count; i++)
{
if(lst[i] == int.Parse(result1))
{
lst[i] = int.Parse(lastno);
break;
}
}
lst = lst2;
}
else if(output == "Collapse")
{
string val = Regex.Replace(des, "[^0-9]", "");
int value = int.Parse(val);
for(int i = 0; i < lst.Count; i+=1)
{
int element = (int)lst[i];
if (element < value)
{
lst.RemoveAt(i);
}
else
{
continue;
}
}
}else if (output == "Mort")
{
PrintValues(lst);
break;
}
}
}
public static void PrintValues(IEnumerable myList)
{
foreach (Object obj in myList)
Console.Write("{0} ", obj);
Console.WriteLine();
}
}
}
more precisely with this part of the code:
else if(output == "Collapse")
{
string val = Regex.Replace(des, "[^0-9]", "");
int value = int.Parse(val);
for(int i = 0; i < lst.Count; i+=1)
{
int element = (int)lst[i];
if (element < value)
{
lst.RemoveAt(i);
}
else
{
continue;
}
}
I tried with all sorts of options but the loop misses elements. as an example
entrance:
1 2 -1 0 -3 9 8 7 2
Collapse 8
Mort
output:
9 8
but the program gives me this output:
2 0 9 8 2
I tried to see what the problem is through the debugger and there I found that the loop misses elements
It should be,
for(int i = 0; i < lst.Count; i+=1)
{
int element = (int)lst[i];
if (element < value)
{
lst.RemoveAt(i);
i--;
}
else
{
continue;
}
}
As #David784 mentioned at the comments. When you remove element the from the itterating collection. You miss 1 element everytime after you delete the i item.
To elaborate on #Berkay's comment: You're changing a list as you're looping through it. If i is 2 and you remove that element, then the element that used to be at index 3 now becomes 2, and so forth. His method of decrementing i will work. Another way would be to use a linq .Where statement instead:
var newList = lst.Where(e=> e>=value).ToList();
You are modifying the collection you are iterating through. This moves elements to other indexes and invalidates the i you determine for following elements in the for header. Better loop backwards
for (int i = lst.Count - 1; i >= 0; i--) {
// Now you can safely remove (or add) elements without affecting
// the index of non yet processed elements.
}
You are still modifying the index of elements ahead, but you are moving backwards, now.
In Visual Studio, you can use the forr code snippet to create a reverse for-loop. Type
forr <tab> <tab>.
(and of course there is the for code snippet for a normal for loop. It will increment the index with i++ instead of i += 1.)
Also, if the list is very long and you are removing many elements, it is worth to think about performance. This approach could move around a great number of elements. Copying the elements you want to keep to a new list (initialized with an appropriate initial capacity), will be more performing in this case.

c# comparing list of IDs

I have a List<Keyword> where Keyword class is:
public string keyword;
public List<int> ids;
public int hidden;
public int live;
public bool worked;
Keyword has its own keyword, a set of 20 ids, live by default is set to 1 and hidden to 0.
I just need to iterate over the whole main List to invalidate those keywords whose number of same ids is greater than 6, so comparing every pair, if the second one has more than 6 ids repeated respect to the first one, hidden is set to 1 and live to 0.
The algorithm is very basic but it takes too long when the main list has many elements.
I'm trying to guess if there could be any method I could use to increase the speed.
The basic algorithm I use is:
foreach (Keyword main_keyword in lista_de_keywords_live)
{
if (main_keyword.worked) {
continue;
}
foreach (Keyword keyword_to_compare in lista_de_keywords_live)
{
if (keyword_to_compare.worked || keyword_to_compare.id == main_keyword.id) continue;
n_ids_same = 0;
foreach (int id in main_keyword.ids)
{
if (keyword_to_compare._lista_models.IndexOf(id) >= 0)
{
if (++n_ids_same >= 6) break;
}
}
if (n_ids_same >= 6)
{
keyword_to_compare.hidden = 1;
keyword_to_compare.live = 0;
keyword_to_compare.worked = true;
}
}
}
The code below is an example of how you would use a HashSet for your problem. However, I would not recommend using it in this scenario. On the other hand, the idea of sorting the ids to make the comparison faster still.
Run it in a Console Project to try it out.
Notice that once I'm done adding new ids to a keyword, I sort them. This makes the comparison faster later on.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace KeywordExample
{
public class Keyword
{
public List<int> ids;
public int hidden;
public int live;
public bool worked;
public Keyword()
{
ids = new List<int>();
hidden = 0;
live = 1;
worked = false;
}
public override string ToString()
{
StringBuilder s = new StringBuilder();
if (ids.Count > 0)
{
s.Append(ids[0]);
for (int i = 1; i < ids.Count; i++)
{
s.Append(',' + ids[i].ToString());
}
}
return s.ToString();
}
}
public class KeywordComparer : EqualityComparer<Keyword>
{
public override bool Equals(Keyword k1, Keyword k2)
{
int equals = 0;
int i = 0;
int j = 0;
//based on sorted ids
while (i < k1.ids.Count && j < k2.ids.Count)
{
if (k1.ids[i] < k2.ids[j])
{
i++;
}
else if (k1.ids[i] > k2.ids[j])
{
j++;
}
else
{
equals++;
i++;
j++;
}
}
return equals >= 6;
}
public override int GetHashCode(Keyword keyword)
{
return 0;//notice that using the same hash for all keywords gives you an O(n^2) time complexity though.
}
}
class Program
{
static void Main(string[] args)
{
List<Keyword> listOfKeywordsLive = new List<Keyword>();
//add some values
Random random = new Random();
int n = 10;
int sizeOfMaxId = 20;
for (int i = 0; i < n; i++)
{
var newKeyword = new Keyword();
for (int j = 0; j < 20; j++)
{
newKeyword.ids.Add(random.Next(sizeOfMaxId) + 1);
}
newKeyword.ids.Sort(); //sorting the ids
listOfKeywordsLive.Add(newKeyword);
}
//solution here
HashSet<Keyword> set = new HashSet<Keyword>(new KeywordComparer());
set.Add(listOfKeywordsLive[0]);
for (int i = 1; i < listOfKeywordsLive.Count; i++)
{
Keyword keywordToCompare = listOfKeywordsLive[i];
if (!set.Add(keywordToCompare))
{
keywordToCompare.hidden = 1;
keywordToCompare.live = 0;
keywordToCompare.worked = true;
}
}
//print all keywords to check
Console.WriteLine(set.Count + "/" + n + " inserted");
foreach (var keyword in set)
{
Console.WriteLine(keyword);
}
}
}
}
The obvious source of inefficiency is the way you calculate intersection of two lists (of ids). The algorithm is O(n^2). This is by the way problem that relational databases solve for every join and your approach would be called loop join. The main efficient strategies are hash join and merge join. For your scenario the latter approach may be better I guess, but you can also try HashSets if you like.
The second source of inefficiency is repeating everything twice. As (a join b) is equal to (b join a), you do not need two cycles over the whole List<Keyword>. Actually, you only need to loop over the non duplicate ones.
Using some code from here, you can write the algorithm like:
Parallel.ForEach(list, k => k.ids.Sort());
List<Keyword> result = new List<Keyword>();
foreach (var k in list)
{
if (result.Any(r => r.ids.IntersectSorted(k.ids, Comparer<int>.Default)
.Skip(5)
.Any()))
{
k.hidden = 1;
k.live = 0;
k.worked = true;
}
else
{
result.Add(k);
}
}
If you replace the linq with just the index manipulation approach (see the link above), it would be a tiny bit faster I guess.

How to determine number of objects in array

Once again I cannot find a solution myself (I have tried using Array.IndexOf(db, accnum) with a pos > -1 return boolean, but have reverted to this loop after I couldn't make it work).
So, I thought using db.Length would leave 'a' at the length of all the non-null elements in the array, however it seems to count the whole array, meaning that when the loop reaches a null element it causes an error. Is there a way to halt the loop count when it runs out of objects in the array?
void withdrawal()
{
int accnum;
double withdrawal;
//get and parse input details
accnum = int.Parse(tbNameEnt.Text);
withdrawal = double.Parse(tbBalaEnt.Text);
//check if account exists within existing objects
int a = db.Length;
for (int i = 0; i < a; i++)
{
if (db[i].GetAccNo() == accnum)
{
pos = i;
//deduct from balance
db[pos].SetBalance(db[pos].GetBalance() - withdrawal);
WithMess(); //success message
hide_form();
MakeWith2.Visible = false;
show_menu();
break;
}
else if ((db[i].GetAccNo() != accnum) && (i == db.Length - 1))
{
//account doesn't exist message
MessageBox.Show("Account does not exist");
}
}
}
If there are null items that pad the array.. break out of the loop when you reach one:
for (int i = 0; i < a; i++) {
if (db[i] == null)
break;
// the rest
Alternatively, use LINQ to filter the null items out:
foreach (var item in db.Where(x => x != null)) {
// loop here
}

Getting access to an ArrayList within an ArrayList

I'm sure the first response will be 'wtf, don't use arraylists!', but I'm really just trying to get this working in any way that I can.
It's basically the 'match looker' for a match 3 game. I'm having trouble getting access to the match data within the match list. See below..
public void FindAndRemoveMatches() {
ArrayList foundMatches = LookForMatches();
//just checking if we're getting the right amount for now
Debug.Log("We found " + foundMatches.Count + " 'Match 3's");
foreach(Object el in foundMatches){
// Debug.Log(el.ToString());
}
}
ArrayList LookForMatches(){
//List<int> matchList = new List<int>();
ArrayList matchList = new ArrayList();
// search for horizontal matches
// note that we're subtracting two rows here.
// We don't need to check the last two rows because we're matching 3.
for (int i = 0; i < BOARD_WIDTH; i++){
for (int j = 0; j < BOARD_HEIGHT-2; j++){
ArrayList match = GetMatchHoriz(i,j);
if (match.Count > 2) {
matchList.Add(match);
i += match.Count-1;
}
}
}
// search for vertical matches
for (int i = 0; i < BOARD_WIDTH; i++){
for (int j = 0; j < BOARD_HEIGHT-2; j++){
ArrayList match = GetMatchVert(i,j);
if (match.Count > 2) {
matchList.Add(match);
j += match.Count-1;
}
}
}
return matchList;
}
// look for horizontal matches starting at this point
ArrayList GetMatchHoriz(int col,int row){
ArrayList match = new ArrayList();
match.Add(mBoard[col,row]);
for(int i = 1; (col+i)<8; i++) {
if (mBoard[col,row] == mBoard[col+i,row]) {
if(mBoard[col+i,row] > mPieces.GetNumPieceTypes()) match.Add(mBoard[col+i,row]);
} else {
return match;
}
}
return match;
}
// look for horizontal matches starting at this point
ArrayList GetMatchVert(int col,int row){
ArrayList match = new ArrayList();
match.Add(mBoard[col,row]);
for(int i = 1; (row+i)<8; i++) {
if (mBoard[col,row] == mBoard[col,row+i]) {
if(mBoard[col,row+i] > mPieces.GetNumPieceTypes()) match.Add(mBoard[col,row+i]);
} else {
return match;
}
}
return match;
}
Good news is, I know that it's finding the matches correctly. The number of matches it's finding using the debug log correlates with what's happening on the screen. Yay! But, I need to get access to that data so I can compare it to the board (mBoard[col,row]) and remove those objects from the game board.
The 'foreach' loop in findandremovematches gives an error about casting. Any help with this?
thanks!
I hope i understand your question correctly
foreach(Objecct obj in list)
{
ArrayList inner = obj as ArrayList;
if(inner != null)
{
//this is what you are looking for
//you can then iterate the inner array list and get the int[,] you saved
}
}
However, as suggested, use List<ArrayList> or List<List<int[,]>> if I guessed it right.
If you're using ArrayLists because you think they're easier, don't. Use List.
If you're using ArrayLists because you have to and there is no way for you to use List (which I doubt) then you'll have something like this:
foreach(ArrayList innerList in foundMatches)
{
foreach(SomeClassThatYouAddToInnerLists item in innerlist)
{
//use item;
}
}
Replace SomeClassThatYouAddToInnerLists with whatever the type is that you're adding to the inner ArrayLists. (The type of mBoard.)
If you use Lists then it's very clear what the type is of everything inside of the list. A List<List<int>> is easy to work with. It's a list of lists of numbers.

Removing controls in a loop

Yesterday I wrote a piece of code to remove all the controls in a form that fulfills certain criteria. Writing it naively, this is what I come up with.
for (int i = 0; i < this.Controls.Count; ++i)
{
if (this.Controls[i].Name.Length == 2)
{
this.Controls.Remove(this.Controls[i);
}
}
But it so happens that the code is wrong. I then change it to:
foreach (Control ctr in this.pbBoardImage.Controls)
{
if (ctr.Length == 2)
{
this.Controls.Remove(ctr);
}
}
But it still wasn't correct.
I know that the correct way would be:
for (int i = this.Controls.Count - 1; i >= 0; i--)
{
if (this.Controls[i].Name.Length == 2)
{
this.Controls.Remove(this.Controls[i]);
}
}
However it still doesn't feel elegant. I couldn't use List.RemoveAll, since this.Controls wasn't a List. So can I ask for a more elegant way, preferably without using a loop?
Not sure why you didn't like this answer... I've highlighted the important RemoveAt; however, as an alternative in .NET 3.5/C# 3.0: LINQ:
var qry = from Control control in Controls
where control.Name.Length == 2
select control;
foreach(var control in qry.ToList()) {
Controls.Remove(control);
}
(original)
You can't Remove within foreach - it breaks the iterator. A common approach here is to iterate backwards:
for (int i = this.Controls.Count - 1; i >= 0; i--) {
if (this.Controls[i].Name.Length == 2) {
this.Controls.RemoveAt(i); // <=========== *** RemoveAt
}
}
This avoids the "off by one" issues, etc.

Categories