using c# I have read an XML file into an XML document and I use XPath to get a list of XMLNodes in an XMLNodeList codelist. I am wanting to create a separate List BlankCodes that has reference to any XmlNodes in codelist that meet a criteria.
so my current code that creates a list of XmlNodes of interest looks like the below.
XmlDocument xDoc = new XmlDocument();
xDoc.Load("C:\\Test.XML");
List<int> blankCodes = new List<int>();
XmlNodeList codeList = xDoc.SelectNodes("codelist\\Code");
foreach( XmlNode aNode in codeList)
{
if(aNode.Value == "")
{
blankCodes.Add( (int)aNode.Attributes("Index")
}
}
I will then iterate through the integers in the blankCodes list and find the corresponding node in the codeList again and modify another value in the node.
is it possible to essentially create a list of Pointers to the appropriate XmlNodes in codeList? then rather than having to find XmlNodes in codeList by xPath or looping through I can reference the XmlNode directly?
I am more than happy to try and provide clarification if you can guide on what needs clarifying.
Many Thanks bommelding.
------Demo code of working answer below -----------------
using System;
using System.Collections.Generic;
namespace TestReferences
{
/// <summary>
/// Basic Class to with index and value
/// </summary>
class aVal
{
public int Index { get; set; }
public int Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
//Create two lists of class to simulate XML node
List<aVal> originalList = new List<aVal>(); //Proper list as if read from XML
List<aVal> blankList = new List<aVal>(); //List of Blank Codes
//Loop to create 20 instances of class
for(int i = 1; i <= 20; i++)
{
//Create class
aVal temp = new aVal();
temp.Index = i; //Index
temp.Value = i * 2; //Easily Identifiable Value
originalList.Add(temp); //Add to original list
if( i == 4 || i==12 || i == 18) //Simulate Blank Codes
{
blankList.Add(originalList[originalList.IndexOf(temp)]); //Add the instance to blank list so we get a reference,
//I presume that I have to add the one in the list not the temporary instance used to populate the original list
}
}
//Write the original list to the console
//Expected output "Index 1 : Val 2"
Console.WriteLine("******* Original List ***************");
foreach( aVal te in originalList)
{
Console.WriteLine("Index {0} : Val {1}", te.Index, te.Value);
}
//Write the blank list to the console.
Console.WriteLine("******* Blank List ***************");
foreach (aVal te in blankList)
{
Console.WriteLine("Index {0} : Val {1}", te.Index, te.Value);
}
Console.WriteLine("*****************");
Console.WriteLine("Modifying Blanks");
Console.WriteLine("*****************");
//Set each instance.value to -99 referenced by the blanklist
foreach (aVal te in blankList)
{
te.Value = -99;
}
//Check the output, 4,12,18 should have value -99
Console.WriteLine("******* Original List after blanks modified ***************");
foreach (aVal te in originalList)
{
Console.WriteLine("Index {0} : Val {1}", te.Index, te.Value);
}
Console.ReadLine();
}
}
}
Yes, because XmlNodes are normal C# objects. You always work with (through) a reference.
Creating a copy of an instance is a lot more work, especially for the XmlDocument family.
List<XmlNode> blankCodes = new List<XmlNode>();
if(aNode.Value == "")
{
blankCodes.Add(aNode);
}
Related
I am creating an application for my C# class in school that reads commands in from text file and displays the results in the console. Basically there will be commands in the file that should add a node and delete a node from a singly linked list. So if in the input file it reads I:1, I:7 it would output Node1:1 and Node2:7 to the console.
I know how to parse a file to display things like hello world, etc. But I am unsure as to how I would parse a file to look for a specific string and then output results based on that string (in this case appending and deleting nodes from a list). Is there any resources on how one would do that?
This is what I have for my program so far:
public class LinkedList {
Node head; //the head of list
public class Node {
public int data;
public Node next;
//constructor
public Node(int d) {
data = d;
next = null;
} //end of constructor
}
public void printList() { //traversing list and printing the contents starting from head(1)
Node n = head;
while (n != null) {
Console.Write(n.data + " ");
n = n.next;
}
}
public void push(int new_data) {
Node new_node = new Node(new_data); //allocate new node, put in data
new_node.next = head; //make next of new node as head
head = new_node; //move the head to point to new node
}
//main method to create a linked list with 3 nodes
public static void Main(String[] args) {
//starting with an empty list
LinkedList llist = new LinkedList();
llist.head = new Node(1);
Node second = new Node(2);
Node third = new Node(3);
//now 3 nodes have been allocated. head, second, and third
llist.head.next = second; //linking the head (1st) with the second node. these are both now linked.
second.next = third; //linking second with third. now linked. all 3 are linked now
llist.printList();
}
} //end of class program
I have a treeview which contains several nodes (over 100.000) with checkboxes, which represent folders/files.
Besides that I have a string list which contains maybe 10-20 elements.
I want to iterate through the all tree and check for each node if the fullpath of the node is contained in that string list, if yes than I set the checkbox for that node.
My problem is with the performance. If the string list contains more than 2-3 elements, it takes ages for the program to show the result.
Here is my code:
void SetCheckedNodes(TreeNodeCollection allNodes, IEnumerable<string> excludedFiles)
{
foreach (TreeNode node in allNodes)
{
foreach (string ef in excludedFiles)
{
if (ef == node.FullPath)
{
node.Checked = true;
}
}
if (node.Nodes.Count > 0)
SetCheckedNodes(node.Nodes, excludedFiles);
}
}
Does anyone have an idea to improve the performance of this code?
This code will check about 1000 items on 100000 almost instantly:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// assuming tree view has a root node already
treeView1.CheckBoxes = true;
treeView1.BeginUpdate();
var builder = new StringBuilder();
// read first 100000 paths from a file
var watch = Stopwatch.StartNew();
var lines = File.ReadAllLines(#"C:\temp\files.txt").Take(100000).ToArray();
builder.AppendLine($"Time taken reading paths: {watch.Elapsed}");
// populate tree view with these paths
watch.Restart();
foreach (var line in lines)
{
var root = treeView1.TopNode;
var keys = line.Split(Path.DirectorySeparatorChar);
foreach (var key in keys)
{
var nodes = root.Nodes;
root = nodes.ContainsKey(key) ? nodes[key] : nodes.Add(key, key);
}
}
builder.AppendLine($"Time taken populating tree: {watch.Elapsed}");
// add some garbage and shuffle
watch.Restart();
var range = Enumerable.Range(0, 1000).ToArray();
var random = new Random();
var strings = range.Select(s => lines[random.Next(lines.Length)]);
var garbage = range.Select(s => s.ToString());
var array = strings.Concat(garbage).OrderBy(s => random.Next());
builder.AppendLine($"Time taken randomizing: {watch.Elapsed}");
// now check checkable items
watch.Restart();
foreach (var line in array)
{
var root = treeView1.TopNode;
var keys = line.Split(Path.DirectorySeparatorChar);
foreach (var key in keys)
{
var nodes = root.Nodes;
root = nodes.ContainsKey(key) ? nodes[key] : null;
if (root == null)
break;
}
if (root == null)
continue;
root.Checked = true;
}
builder.AppendLine($"Time taken checking items: {watch.Elapsed}");
treeView1.EndUpdate();
MessageBox.Show(builder.ToString());
}
}
}
Differences with release build are really negligible:
So it's a different approach (written from scratch) that might give you an idea or two.
You do a linear search in a collection, that's slow by default.
Replace IEnumerable<string> with IHashset<string>,
and the inner foreach with excludedFiles.Contains(node.FullPath)
and this will boost it.
Also the "continue;" is kind of strange; it's doing absolutly nothing. A "break;" would have made more sense. You can stop searching after you found it, there is no reason to continue. But with a hashset you don't need that anyway.
I have to store two types of information in any data structure for what I came up with the scrap solution of 2D array in C#. I have to store as:
number of cluster in int data type
cluster membership count in int data type
If I use a 2D array as:
Int32[,] _clusterMembership = new Int32[10, 10];
But the issue here is:
I don't know what total number of cluster will be?
I don't know what number of members each cluster will have?
So the question is:
How can I manage to store this information in C# ?
ADDENDUM
I have to use answer from this question here in this method as:
public static List<Cluster> DP_Cluster(List<string> _customer, double _alpha)
{
var _currentClusters = 0; // current number of clusters i.e. "k"
var _memberNumber = 0; // running member number i.e. "n"
//var _dic = new Dictionary<int, List<string>>();
var _probOld = 0.0;
var _probNew = 0.0;
List<Cluster> myClusters = new List<Cluster>();
Cluster cluster = new Cluster(_currentClusters += 1);
cluster.Members.Add(new ClusterMember { Name = _customer.ElementAt(_memberNumber) });
myClusters.Add(cluster);
//_dic.Add(_currentClusters, _customer.ElementAt(_memberNumber));
_currentClusters += 1;
for(int _i = 1; _i < _customer.Count - 1; _i++)
{
if( _i <= _currentClusters)
{
_probOld = myClusters[_i].Members.Count / ((_i+1) - 1 + _alpha);
}
else
{
_probNew = _alpha / ((_i+1) - 1 + _alpha);
}
if(_probNew > _probOld)
{
// Add _customer.ElementAt(_memberNumber+=1) to New Cluster
Cluster cluster = new Cluster( _currentClusters += 1 ); // Here is an error as we defining same name for another cluster
myClusters.Add(cluster);
}
else
{
// Add _customer.ElementAt(_memberNumber+=1) to Old Cluster
}
}
return myClusters;
}
How should I update my method to get desired results?
You should consider making two types, one for the clusters and one for the members:
Members
public class ClusterMember
{
public string Name {get;set;}
// more properties...
}
Clusters
public class Cluster
{
public int ID {get;}
public List<ClusterMember> Members {get;}
public Cluster(int id)
{
ID = id;
Members = new List<ClusterMember();
}
}
And then you can store your clusters in a list
List<Cluster> myClusters = new List<Cluster>();
Cluster cluster = new Cluster(1);
cluster.Members.Add(new ClusterMember { Name = "Member1" });
myClusters.Add(cluster);
UPDATE I assumed that you want to do more with your data than just store these two information and tried to provide a better object-oriented approach.
To get your counts:
int totalNumberOfClusters = myClusters.Count;
int numberOfMembersInOneCluster = cluster.Members.Count;
int totalNumberOfClusterMembers = myClusters.Sum(c => c.Members.Count);
And to output the number of members for each cluster:
foreach(Cluster c in myClusters)
Console.WriteLine($"Cluster {c.ID} has {c.Members.Count} members.");
As is already mentioned. You can simply use a list. The bottom code example shows how to create the list type you need and how you add and access values in that list.
using System.IO;
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
//Creating a list of lists that contains integers
List<List<int>> clusters = new List<List<int>>();
//each list in the above list consists of a list of integers. So we need to add list of integers to that list
List<int> row = new List<int>();
//now we add integers to the list
row.Add(1); row.Add(2); row.Add(3); row.Add(4);
//Now we add the list of integers to the list of lists of integers
clusters.Add(row);
foreach(List<int> rows in clusters)
{
foreach(int num in rows)
{
System.Console.WriteLine(num);
}
}
Console.WriteLine("number of rows: {0}", clusters.Count);
Console.WriteLine("number of elements in the first row: {0}", clusters[0].Count);
}
}
You could consider using a list of lists;
List<List<int>> clusters;
See this answer on another question for more info and also how to make it into a more generic class: https://stackoverflow.com/a/1596563/6065552
You can store your data in a list which has keyvaluepair items.
Or use Dictionary
List<KeyValuePair<int, int>>()
Dictionary<int,int>();
so you can add new keyvaluepair for each cluster.
static void Main(string[] args)
{
var clusterMembership = new Dictionary<int, int>();
//Add cluster 123 and assign a member count of 4
clusterMembership.Add(123, 4);
//Change member count for cluster 123 to 5
clusterMembership[123] = 5;
//Remove cluster 123
clusterMembership.Remove(123);
//Get the number of clusters in the dictionary
var count = clusterMembership.Count;
//Iterate through the dictionary
foreach(var clusterKey in clusterMembership.Keys)
{
var memberCount = clusterMembership[clusterKey];
}
}
I have a List<Node> nodes and each node in nodes have a variable called Interest associated with it.
public static List<Node> nodes = new List<Node>();
for (int i = 0; i < Program.n; i++)
{
//int densityrandom = generateRandom();
Node n = new Node(Interest3);
Program.nodes.Add(n);
}
While class Node is as Follows:
public class Node
{
public int Density { get; set; }
public InterestProfile P1 { get; set; }
public List<Edge> adjnodes { get; set; }
public Node(InterestProfile a)
{
adjnodes = new List<Edge>();
P1 = a;
}
}
For some object Interest1 of type InterestProfile there is an array associated with it like
Interest1 = new InterestProfile(array1);
So, if you do something like nodes[0].P1.InterestArray[0], it will give me first element in the array1.
Now here is my actual question:
I am splitting up the list nodes according to user input into list of list of Node. Thus, if nodes have 100 elements in it, it will be split up into 10 lists and all those lists will stored in one list.
This is something I have in mind:
List<List<Node>> smallList = new List<List<Node>>();
for (int i = 0; i < nodes.Count; i++)
{
for(int k=0; k < smallList.Count; k++)
{
if (list[i].P1.InterestArray[0] > 5 && smallList[k].Count != 10)
{
smallList[0].Add(list[i]);
}
else smallList[k+1].Add(list[i]);
}
and so on by checking for all 5 elements in an array associated with particular node. However, when smallList[0] reaches limit of 10, I want to start adding elements from nodes in some other smallList[i]. When all the values of particular InterestArray[j] are checked for all nodes in nodes, then only it should move to checking InterestArray[j+1]. How can I program this in c#? Is my implementation correct and if yes can it be improved?
I know question is quite confusing. Please ask me if anything is difficult to understand. Any help will be highly appreciated.
Instead of creating the List<List<Node>> data structure why not simply query your existing List<Node> for the nodes you want when you need them?
e.g.
Old School:
var interestingNodes = new List<Node>();
foreach(Node node in nodes)
{
foreach(int i in note.InterestArray)
{
if(i > 5)
interestingNodes.Add(node);
}
}
Using List<T>.FindAll():
var interstingNodes = nodes.FindAll(node => node.P1.InterestArray.Contains(5));
Or in LINQ:
var interstingNodes = from node in nodes
where node.P1.InterestArray.Contains(5)
select node;
I need to get an object and check if it already exists.
In case it does, I want to add a number, or increase the number in its name.
For example if I have the object "a" and it exists, I need to add a 1 so it's called a1.
In case a1 exists, a2, etc.
How could I perform this?
My code:
if (e.TreeNode.Tag is Variant)
{
if (variantExists(e.TreeNode.Text))
{
Random r = new Random();
int randomNumber = r.Next(0, 99);
e.TreeNode.Text = e.TreeNode.Text + randomNumber;
//e.TreeNode.Remove();
return;
}
}
Can you change the TreeNode class? I would add properties for Label (Name without Index) and Index and make the Name property read only, i.e
class TreeNode
{
public int Index {get;set;}
public string Label {get;set;}
public string Name
{
get { return Index == 0 ? Label : Label + Index; }
}
}
In your code you just need to set the Index property to the value you need and dont worry about the whole string parsing stuff
string name = "SomeName";
string tempName = name;
int n = 0;
while (DoesNameExist(tempName))
{
n++;
tempName = name + n;
}
name = tempName;
This gets ineffecient for large numbers of the same object, but that shouldn't happen right?
The problem with doing it the other way around, and stripping off trailing numbers to find the "original" name is that the original name may genuinely have numbers on it.
Eg. You say you add:
SomeName
SomeName99
SomeName
SomeName99
The above code will give you
SomeName
SomeName1
SomeName99
SomeName991
Something along the lines of this could work:
var existingItems = new HashSet<string>();
var items = new List<string>{"a", "b", "a"};
foreach (var item in items)
{
var tempItem = item;
var i = 1;
while (!existingItems.Add(tempItem))
tempItem = tempItem + i++;
}