preventing duplicates when inserting nodes to treeview control - c#

I want to create a hierarchical view of strings based on first two characters.
If the strings are:
AAAA,AAAA,BBDD,AABB,AACC,BBDD,BBEE
I want to reate a treeview that looks like this:
AA
AAAA
AABB
AACC
BB
BBDD
BBEE
I currently have some code that looks like this (inside a loop over the strings):
TreeNode pfxNode;
if (treeView1.Nodes[pfx]!=null) {
pfxNode = treeView1.Nodes[pfx];
}
else {
pfxNode = treeView1.Nodes.Add(pfx);
}
if (!pfxNode.Nodes.ContainsKey(string)) {
pfxNode.Nodes.Add(string, string + " some info");
}
For some reason this ends up with multiple "AA" nodes at the top level.
What am I missing?
please no pre-filtering of strings I want to be able to check if a specific treenode exists based on its key.
thanks

else {
pfxNode = treeView1.Nodes.Add(pfx);
}
There's your mistake, you are forgetting to set the key of the tree node. So the next ContainsKey() won't find it. Fix:
pfxNode = treeView1.Nodes.Add(pfx, pfx);

Use this:
var q = from s in arr
group s by s.Substring(0, 2) into g
select new
{
Parent = g.Key,
Children = g.Select (x => x).Distinct()
};
foreach (var item in q)
{
var p = new TreeNode(item.Parent);
TreeView1.Nodes.Add(p);
foreach (var item2 in item.Children)
p.Nodes.Add(new TreeNode(item2));
}

Related

Updating List<string> values using linq C#

I have a list of strings that I would like to iterate through and change the values if certain items in the list if they match up to a string value in a separate list of objects.
User inputs an email address into an Event object that contains a list of EventMembers:
List<string> EventMembers
I would then like to check through all users in the database to find the username(e-mail address) that matches with the inputted e-mail address
i understand I cannot change values in a list using a foreach loop, but i'm lost with what to do with linq. Basically i'm trying to do something like this:
var allUsers = _userManager.Users
foreach (var a in allUsers)
{
foreach (var e in #event.EventMembers)
{
if (e == a.UserName)
{
e = a.FirstName + a.LastName;
}
}
}
The best thing would be to define an initial collection of members so you don't keep modifying the list while the foreach is still running. You could then check if EventMembers contain the username and then replace it by accessing the value with the index.
var allUsers = _userManager.Users;
List<string> Members;
foreach (var a in allUsers)
{
if (#event.EventMembers.Contains(a.UserName))
{
var index = #event.Members.IndexOf(a.UserName);
Members[index] = a.FirstName + a.LastName;
}
}
EventMembers = Members;

Removing an element from list if it contains particular text in it

I have a C# method in which I look for certain text say username in a list with element in the format username + datetime and if any part of text matches the element in the list, then the entire element has to be removed from the list
Method to add to the c# List
string active_user = model.UserName.ToString();
string datetime = "(" + DateTime.Now + ")";
List<string> activeUsers = new List<string>();
if (activeUsers.Any(str => str.Contains(active_user)))
{
//do nothing
}
else
{
activeUsers.Add(active_user+datetime);
}
Now I would like a method that deletes the element if it matches the username or any part of element something like
if (activeUsers.Contains(active_user))
{
activeUsers.Remove(active_user);
}
While the other answers are correct, you should note that they will delete any matches. For example, active_user = "John" will remove "John", "John123", "OtherJohn", etc.
You can use regular expressions to test, or if user names don't have parentheses, do your test like this:
string comp = active_user + "("; // The ( is the start of the date part
activeUsers.RemoveAll(u => u.StartsWith(comp));
Also note, this is case sensitive.
You can do something like
activeUsers.RemoveAll(u => u.Contains(active_user));
That will match and remove all elements of activeUser that contain the text in active_user.
var user = activeUsers.FirstOrDefault(au => au.Contains(active_user);
if(user != null)
activeUsers.Remove(user);
if you are only wanting to remove the first match, else :
var users = activeUsers.Where(au => au.Contains(active_user);
foreach(var user in users)
activeUsers.Remove(user);
Or more simply, the RemoveAll method Eric answered with.
If i Want to remove Numeric String Values List Items from my List
List<ModelName> ModelList = new List<ModelName>();
var regex = new Regex(#"\d");
foreach(var item in ModelList.ToList())
{
if (regex.IsMatch(item.PropertyName))
{
ModelList.RemoveAll(t => t.PropertyName== item.PropertyName);//Or
ModelList.RemoveAll(t => t.PropertyName.Contains(item.PropertyName));//Or You Can Use Contains Method
}
}
return ModelList;
This will remove all items from list those having Numeric values as a string and return only Character String in List items

How to convert the following foreach nested loop to LINQ?

Code:
foreach (var item in items.Children)
{
RadTreeViewItem parent1 = new RadTreeViewItem();
parent1.Header = NodeHeader(item.Path, item.Name, SelectedPath, ProjectData);
parent1.Tag = item;
foreach (var child in item.Children)
{
RadTreeViewItem children = new RadTreeViewItem();
children.Header = NodeHeader(child.Path, child.Name, SelectedPath, ProjectData);
children.Tag = child;
parent1.Items.Add(children);
}
Parent.Items.Add(parent1);
}
items.Children and item.Children are ObservableCollection<>
parent1.Header and children.Header are HeaderedItemsControl.Header
parent1.Tag and children.Tag are FrameworkElement.Tag
How to convert the above foreach nested loop to LINQ ?
LINQ... Language INtegrated (and here's the key bit) Query.
What you are doing is not a query. Leave it with the foreach loops; it is fine, it is clear, it is obvious.
In particular, the .Items.Add methods on the various collections is not really something you can trivially reproduce in LINQ. You can probably do it, but it will be ugly and hard to maintain. What you have is fine. If you want to change it for change's sake, maybe:
RadTreeViewItem parent1 = new RadTreeViewItem {
Header = NodeHeader(item.Path, item.Name, SelectedPath, ProjectData),
Tag = item
};
foreach (var child in item.Children)
{
parent1.Items.Add(new RadTreeViewItem {
Header = NodeHeader(child.Path, child.Name, SelectedPath, ProjectData),
Tag = child
});
}
Parent.Items.Add(parent1);
Not exactly an improvement, IMO.

Cant resolve infinite recursion

Ive recently started coding on populating a treeview control from a list and this is where i am starting.
http://msmvps.com/blogs/deborahk/archive/2009/11/09/populating-a-treeview-control-from-a-list.aspx
I am currently trying to retrieve registry paths and storing them to a list. From that list, I am trying to add it as parent nodes to the treeview control. Then I traverse every level in the registry and add them as nodes eventually creating a tree-like replica of it. I am retrieving registry information through a separate process and I shy away from using the Registry API within c#. I incorporated all of the code in the link above to my current code and now i am experiencing an infinite recursion error everytime i compile.
This is the current code I am working on.
private void registry()
{
string hivelist_output = process.StandardOutput.ReadToEnd();
string[] hivelist_lines = Regex.Split(hivelist_output, "\r\n");
string[] registry_paths = new string[hivelist_lines.Length];
List<string> registry_path_list = new List<string>();
for (i = 0; i < hivelist_lines.Length; i++)
{
registry_paths[i] = hivelist_lines[i].Substring(22);
registry_path_list.Add(registry_paths[i].Trim());
}
for (i = 0; i < registry_path_list.Count; i++)
{
treeViewList.Add(new TreeViewItem()
{
ParentID = 0,
ID = i,
Text = registry_path_list[i]
});
}
PopulateTreeView(0, null);
treeView1.ExpandAll();
}
private void PopulateTreeView(int parentId, TreeNode parentNode)
{
var filteredItems = treeViewList.Where(item => item.ParentID == parentId);
TreeNode childNode = new TreeNode();
foreach (var i in filteredItems.ToList())
{
if (parentNode == null)
childNode = treeView1.Nodes.Add(i.Text);
else
childNode = parentNode.Nodes.Add(i.Text);
PopulateTreeView(i.ID, childNode);
}
}
The error of infinite recursion points me to this line of code and I dont understand why.
childNode = parentNode.Nodes.Add(i.Text);
I appreciate your great advice on this matter. Thanks in advance!
-------------------------------------SOLVED-------------------------------------------
Moving forward, now my next problem is to add the subkeys below each parent node (registry path) and as well as the subkeys below each of the previously retrieved subkeys and key values and data types. Since the registry is quite deep, how could i make it so that I plot all possible values without using too much nested loops? Thanks!
*structure of what I would like to achieve
registry path
- subkey1
- subkeys of subkey1
- subkeys of subkey1.1
- data types & values
-subkey 2
.
.
.
-subkey 3
.
.
.
-subkey n
- data types & values
so and so forth
You are adding TreeViewItems always with ID = 0. Therefore, your PopulateTreeView always calls itself recursively with ID 0.
You need to fix your setup method:
for (i = 0; i < registry_path_list.Count; i++)
{
treeViewList.Add(new TreeViewItem()
{
ParentID = 0,
ID = 0,
Text = registry_path_list[i]
});
}
and use the corresponding IDs.

Looping throught XML element to add data

I dont know how exactly to word my question, so apologies from up front. I have an xml file and it has elements like the following:
- <Allow_BenGrade>
<Amount BenListID="0">0</Amount>
</Allow_BenGrade>
- <Add_Earnings_NonTaxable>
<Amount AddEarnID="0">0</Amount>
</Add_Earnings_NonTaxable>
I am interested in Allow_BenGrade where i can add multiple elements inside there. I have list of 3 items but when I loop through to write it to the file, it only writes the last item in the list, so instead of have 3 elements inside Allow_BenGrade, i end up having one (last one in the item list). My code is below. Please help thank you.
var query = from nm in xelement.Elements("EmployeeFinance")
select new Allowance {
a_empersonalID = (int)nm.Element("EmpPersonal_Id"),
a_allbengradeID = (int)nm.Element("Grade_Id")
};
var x = query.ToList();
foreach (var xEle in x)
{
var qryBenListGrade = from ee in context.Employee_Employ
join abg in context.All_Inc_Ben_Grade
on ee.Grade_Id equals abg.GradeID
join abl in context.All_Inc_Ben_Listing
on abg.All_Inc_Ben_ListingID equals abl.ID
where ee.Employee_Personal_InfoEmp_id == xEle.a_empersonalID && abg.GradeID == xEle.a_allbengradeID && (abl.Part_of_basic == "N" && abl.Status == "A" && abl.Type_of_earnings == 2)
//abl.Approved_on !=null &&
select new
{
abl.ID,
abl.Amount,
abg.GradeID,
ee.Employee_Personal_InfoEmp_id,
abl.Per_Non_Taxable,
abl.Per_Taxable
};
var y = qryBenListGrade.ToList();
//xEle.a_Amount = 0;
foreach (var tt in y)
{
Debug.WriteLine("amount: " + tt.Amount + " emp id: " + tt.Employee_Personal_InfoEmp_id + " ben list id: " + tt.ID);
// xEle.a_Amount = xEle.a_Amount + tt.Amount;
var result = from element in doc.Descendants("EmployeeFinance")
where int.Parse(element.Element("EmpPersonal_Id").Value) == tt.Employee_Personal_InfoEmp_id
select element;
foreach (var ele in result)
{
ele.Element("Allow_BenGrade").SetElementValue("Amount", tt.Amount);
//ele.Element("Allow_BenGrade").Element("Amount").SetAttributeValue("BenListID", tt.ID);
}
}
doc.Save(GlobalClass.GlobalUrl);
}
SetElementValue will, as the name suggests, set the value of the Amount element... You need to Add a new one instead:
ele.Element("Allow_BenGrade").Add(new XElement("Amount",
new XAttribute("BenListID", tt.ID),
tt.Amount);
Let me know if that solves it for you.
The XElement.SetElementValue Method:
Sets the value of a child element, adds a child element, or removes a
child element.
Also:
The value is assigned to the first child element with the specified
name. If no child element with the specified name exists, a new child
element is added. If the value is null, the first child element with
the specified name, if any, is deleted.
This method does not add child nodes or attributes to the specified
child element.
You should use the XElement.Add Method instead.

Categories