I have a dictionary where values are stored in the following format -
userID, empDetails
For example,
1234, 'empName,jobDesc,CardNumber,Type'
I have to compare this information with another set of information such that -
If entered userId is present in the above dictionary, then remove this record from the dictionary.
If entered CardNumber is present (here userId is not known) in the above dictionary, then remove this record from the dictionary.
The first condition is simple and can be done by
dictionary.Remove(key)
But I am confused as to how would I implement the second condition. I want something like
if(CardNumber.PresentinAboveDictionary)
then
Remove that record
I know we can compare a partial string in a key like this, but I want to remove the record.
Check if any part of a hashtable value contains certain string c#
Assuming the employment details in your dictionary are a string in the specified format you would need to:
Search the values within the dictionary
Parse/Split the values to get the card numbers
Check the card numbers to see if they match the card number you are checking
Return the key value pair when a match occurs
Remove the entry for the key in the returned key value pair
Example code for the solution:
var dictionary = new Dictionary<int, string>() { { 1, "empName,jobDesc,124124134,Type" } };
var cardNumber = 124124134;
var entry = dictionary.FirstOrDefault(x => DoEmploymentDetailsContainCardNumber(x.Value, cardNumber));
if (!entry.Equals(default(KeyValuePair<int, string>)))
{
dictionary.Remove(entry.Key);
}
Method that checks if card number is present in employment details:
private static bool DoEmploymentDetailsContainCardNumber(string empDetails, int cardNumber)
{
var splitEmpDetails = empDetails.Split(',');
var empDetailsCardNumber = splitEmpDetails[2];
return empDetailsCardNumber == cardNumber.ToString();
}
Instead of Dictionary you can use a strongly typed List
Use the Linq builtin Remove method
Use Parallel.ForEach, iterate the list and remove the item (beware, takes more time)
pseudo code:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using System.Collections;
namespace ConsoleApp4
{
public class Employee
{
public Employee(int userID, string empDetails)
{
string[] props = empDetails.Split(new char[] { ',' }, StringSplitOptions.None);
this.userID = userID;
this.empName = props[0];
this.jobDesc = props[1];
this.CardNumber = props[2];
this.Type = props[3];
}
public int userID { get; set; }
public string empName { get; set; }
public string jobDesc { get; set; }
public string CardNumber { get; set; }
public string Type { get; set; }
}
public class MyCustomList : List<Employee>
{
public void Add(int userID, string empDetails)
{
this.Add(new Employee(userID, empDetails));
}
public bool Remove(string CardNumber)
{
bool found = false ;
Parallel.ForEach(this,
(i, state) =>
{
if (i.CardNumber == CardNumber)
{
this.Remove(i);
state.Break();
}
});
return found;
}
public bool RemoveV2(string CardNumber)
{
bool found = false;
if (this.Any(x => x.CardNumber == CardNumber))
{
this.Remove(this.Where(x => x.CardNumber == CardNumber).First());
found = true;
}
return found;
}
}
class Program
{
static void Main(string[] args)
{
var dict = new MyCustomList();//userID, empDetails list
dict.Add(12341, "empName1,jobDesc,CardNumber1,Type");
dict.Add(12342, "empName2,jobDesc,CardNumber2,Type");
dict.Add(12343, "empName3,jobDesc,CardNumber3,Type");
dict.Add(12344, "empName4,jobDesc,CardNumber4,Type");
dict.Add(12345, "empName5,jobDesc,CardNumber5,Type");
dict.Add(12346, "empName6,jobDesc,CardNumber6,Type");
dict.Add(12347, "empName7,jobDesc,CardNumber7,Type");
dict.Add(12348, "empName8,jobDesc,CardNumber8,Type");
//remove CardNumber5
dict.Remove("CardNumber5");
Console.Write(dict);
}
}
}
you can follow the simple approach to remove the key by using a loop here.
Here I am assuming that there is no key with a value of -1 in the dictionary.
int keyToRemove = -1;
foreach (var entry in dictionary)
{
if (entry.Value.Contains(CardNumber))
{
keyToRemove = entry.Key;
break;
}
}
if (keyToRemove != -1)
{
dictionary.Remove(keyToRemove);
}
This is possibly overkill and is not optimised for reading the full dataset repeatedly but it is considerably faster than the accepted solution. I put together a test of the solution below which did the following:
Generated 1,000,000 data rows with unique IDs and card numbers (the solution would also work if the card numbers were not unique)
Randomly removed 100,000 data items by ID and 100,000 data items by card number
Generated a list of the remaining data items
The process took around 75 seconds.
I then tried to repeat steps 1) and 2) using the accepted answer - after around 10 minutes it's about 7% of the way through removing data items. Therefore I think the solution below is around 2 orders of magnitude faster for this type of operation.
There are probably better doubley linked list implementations out there but I am not too familiar with any of them.
namespace Question
{
public class EmployeeCollection
{
private readonly Dictionary<int, ListNode<EmployeeDetails>> _idDictionary = new();
private readonly Dictionary<string, Dictionary<int, EmployeeDetails>> _cardNumberDictionary = new();
private readonly LinkedList<EmployeeDetails> _list = new();
public void AddEmployee(EmployeeDetails details)
{
var node = new ListNode<EmployeeDetails>(details);
_list.AddToStart(node);
_idDictionary.Add(details.Id, node);
if(!_cardNumberDictionary.ContainsKey(details.CardNumber))
{
_cardNumberDictionary.Add(details.CardNumber, new Dictionary<int, EmployeeDetails>());
}
_cardNumberDictionary[details.CardNumber].Add(details.Id, details);
}
public void RemoveById(int id)
{
if (_idDictionary.TryGetValue(id, out var node))
{
_idDictionary.Remove(id);
_list.Remove(node);
var list = _cardNumberDictionary[node.Value.CardNumber];
list.Remove(id);
if(list.Count == 0)
{
_cardNumberDictionary.Remove(node.Value.CardNumber);
}
}
}
public void RemoveByCardNumber(string cardNumber)
{
if (_cardNumberDictionary.TryGetValue(cardNumber, out var employees))
{
_cardNumberDictionary.Remove(cardNumber);
foreach (var employee in employees)
{
if (_idDictionary.TryGetValue(employee.Key, out var node))
{
_list.Remove(node);
}
}
}
}
public IEnumerable<EmployeeDetails> Employees => _list.GetAllValues();
public EmployeeDetails? GetById(int id)
{
if(_idDictionary.ContainsKey(id))
{
return _idDictionary[id].Value;
}
return null;
}
}
public class EmployeeDetails
{
public int Id { get; init; }
public string Name { get; init; }
public string JobDescription { get; init; }
public string CardNumber { get; init; }
public string Type { get; init; }
public static EmployeeDetails FromData(int id, string details)
{
var parts = details.Split(',');
return new EmployeeDetails
{
Id = id,
Name = parts[0],
JobDescription = parts[1],
CardNumber = parts[2],
Type = parts[3],
};
}
}
public class LinkedList<T>
{
public int Count { get; private set; }
private ListNode<T>? Start { get; set; }
private ListNode<T>? End { get; set; }
public bool IsEmpty => Count == 0;
public void AddToStart(ListNode<T> node)
{
ArgumentNullException.ThrowIfNull(nameof(node));
node.Next = null;
node.Previous = null;
if (IsEmpty)
{
Start = End = node;
}
else
{
Start!.Previous = node;
node.Next = Start;
Start = node;
}
Count++;
}
public void Remove(ListNode<T> node)
{
if (node != Start)
{
node.Previous!.Next = node.Next;
}
else
{
Start = node.Next;
}
if (node != End)
{
node.Next!.Previous = node.Previous;
}
else
{
End = node.Previous;
}
Count--;
}
public IEnumerable<T> GetAllValues()
{
var counter = Start;
while (counter != null)
{
yield return counter.Value;
counter = counter.Next;
}
}
}
public class ListNode<T>
{
public T Value { get; }
public ListNode<T>? Previous { get; set; }
public ListNode<T>? Next { get; set; }
public ListNode(T value)
{
Value = value;
}
}
}
you can do something like this.
var recordsToRemove = dictionary.Where(x => x.Value.Contains("what you are looking for"))
.ToList();
if (recordsToRemove.Any())
{
foreach (var record in recordsToRemove)
{
dictionary.Remove(record.Key);
}
}
Related
I have an author class like this
public class Author : IEquatable<Author>
{
public string Name { get; private set; }
public List<PublicationData> Publications { get; private set };
public Author(string name, List<PublicationData> publications)
{
Name = name;
Publications = publications;
}
public override string ToString()
{
string lines = Name + "\n";
foreach (PublicationData publication in Publications)
lines += publication.ToString() + "\n";
return lines;
}
public bool Equals(Author other)
{
return Name.Equals(other.Name);
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
And I have publication class like this
public class PublicationData : IEquatable<PublicationData>
{
public string Code { get; set; }
public int Amount { get; set; }
public int Subscription_Length { get; set; }
public PublicationData(string name, int amount, int subscription_Length)
{
Code = name;
Amount = amount;
Subscription_Length = subscription_Length;
}
public override string ToString()
{
return String.Format($"{Code}, {Amount}, {Subscription_Length}");
}
public bool Equals(PublicationData other)
{
return Code.Equals(other.Code);
}
public override int GetHashCode()
{
return Code.GetHashCode();
}
}
Then I have a list of authors that look like this:
AuthorA
- PublicationA
AuthorB
- PublicationB
AuthorB
- PublicationC
I want to get something like this as a new object:
AuthorA
- PublicationA
AuthorB
- PublicationB
- PublicationC
I assume the code should look something like this:
var filtered = authors.Select(nn => new Author
(
nn.Name,
// merge publication lists
)).Distinct()
.ToList();
I just have no idea how do I do this. Can anyone suggest something?
It seems that you are looking for GroupBy method:
authors
.GroupBy(a => a.Name)
.Select(g => new Author(
g.Key,
g.SelectMany(ga => ga.Publications).ToList()))
You can also achieve it via the following code.
var query = from author in authors
group author by author.AuthorName;
foreach (var group in query)
{
Console.WriteLine(group.Key);
// Nested foreach is required to access group items.
foreach (var g in group)
{
Console.WriteLine($"\t{g.Publication}");
}
}
Here is a document Group query results you can refer to.
I have an array of strings separated by "!". I am trying to break that string up and create a tree hierarchy recursively in my custom class called PivotGroup. For example, what I am aiming at is to break up string array
string[] paths = new string[] {
"ROOT!ZZZ!AAA!EEE!15712",
"ROOT!ZZZ!AAA!EEE!15722",
"ROOT!ZZZ!AAA!EEE!13891"}
Into the PivotGroup class such as PivotGroup contains ChildGroups[] that embed the array strings.
So for example:
PivotGroup pgGroup = new PivotGroup();
pgGroup.ChildGroups[0] = PivotGroup[]; // Key:Book Level 3 Value: "AAA"
Now within Book Level 3 ChildGroups I need to set Book Level 4 which value is "EEE" and within the ChildGroups of "EEE" I would need to create another childGroup array which size in the case would be 3 called Book Level 5 and set another PivotGroup for each of following 15712, 15722, 13891
Here is my PivotGroup Class and embedded class Objects:
public class PivotGroup
{
public PivotGroup() { }
public PivotGroup(PivotGroupKey groupKey, PivotRow data, PivotGroup[] childGroups, bool leaf, int groupLevel)
{
GroupKey = groupKey;
Data = data;
ChildGroups = childGroups;
Leaf = leaf;
GroupLevel = groupLevel;
}
public PivotGroupKey GroupKey { get; private set; }
public PivotRow Data { get; private set; }
public PivotGroup[] ChildGroups { get; set; }
public bool Leaf { get; private set; }
public int GroupLevel { get; private set; }
public override string ToString()
{
return GroupKey + ", GroupLevel: " + GroupLevel + ", Children: " +
ChildGroups.Length + (Leaf ? " (Leaf)" : "");
}
}
public class PivotGroupKey
{
public PivotGroupKey()
{
}
public PivotGroupKey(string keyGroup, string keyValue)
{
if(keyGroup != null)
KeyGroup = string.Intern(keyGroup);
if (keyValue != null)
KeyValue = string.Intern(keyValue);
}
public string KeyGroup { get; private set; }
public string KeyValue { get; private set; }
public override string ToString()
{
return KeyGroup + ": " + KeyValue;
}
}
public class PivotRow
{
public PivotRow()
{
}
public PivotRow(string key, params object[] data) : this(key, true, data) { }
public PivotRow(string key, bool entitled, params object[] data)
{
Data = data;
Key = null;
Entitled = entitled;
}
public object[] Data { get; private set; }
public bool Entitled { get; private set; }
public string Key { get { return null; } set { } }
}
Main program I tried:
public class BookLevels
{
public string Root { get; set; }
public string BookLevel2 { get; set; }
public string BookLevel3 { get; set; }
public string BookLevel4 { get; set; }
public string BookLevel5 { get; set; }
}
class Program
{
static void BuildTree(string[] paths)
{
var BookPaths = paths.Select(x => x.Split('!'))
.Select(x => new BookLevels
{
Root = x[0],
BookLevel2 = x[1],
BookLevel3 = x[2],
BookLevel4 = x[3],
BookLevel5 = x[4]
}).GroupBy(z => new { z.BookLevel3, z.BookLevel4 }).ToArray();
var BookLevel3Cnt = BookPaths.Select(q => q.Key.BookLevel3).Count();
PivotGroup root = new PivotGroup(
new PivotGroupKey("Total", ""),
new PivotRow(null, new string[8]),
new PivotGroup[BookLevel3Cnt], false, 0);
foreach (var booklevel3 in BookPaths)
{
AddChildren(root, booklevel3);
}
}
private static void AddChildren(PivotGroup root, IGrouping<object, BookLevels> booklevel, int index = 0)
{
root.ChildGroups[index] = new PivotGroup(
new PivotGroupKey("Book Level " + (index + 3).ToString(), booklevel.Key.ToString()),
new PivotRow(null, new string[8]),
AddChildren(root, booklevel[index], index + 1), false, 0);
}
static void Main(string[] args)
{
string[] paths = new string[] {
"ROOT!ZZZ!AAA!EEE!15712",
"ROOT!ZZZ!AAA!EEE!15722",
"ROOT!ZZZ!AAA!EEE!13891",
"ROOT!ZZZ!AAA!DDD!15712",
"ROOT!ZZZ!AAA!DDD!15722",
"ROOT!ZZZ!AAA!DDD!13891",
"ROOT!ZZZ!BBB!DDD!15812",
"ROOT!ZZZ!BBB!DDD!15822",
"ROOT!ZZZ!BBB!DDD!13891",
};
BuildTree(paths);
Console.WriteLine();
Console.ReadLine();
}
I think my issue might be the way I am creating the Linq statement that breaks up the string, since I'm not sure how to progress thru it recursively.
I'm not sure what goes into which property. Also, for sake of simplicity and to be able to concentrate on the recursive algorithm, I redefine the group class like this (it does not mean that you have to change your class, instead, adapt my algorithm):
public class PivotGroup
{
public string Key { get; set; }
public List<PivotGroup> ChildGroups { get; } = new List<PivotGroup>();
public override string ToString() => Key; // Makes debugging easier.
}
The idea is that the values of the path go into the key. I made ChildGroups a list to be able to add children successively. My BuildTree returns the root
static PivotGroup BuildTree(string[] paths)
{
var root = new PivotGroup { Key = "ROOT" };
foreach (string path in paths) {
AddChildren(root, path.Split('!').Skip(1).ToList());
}
return root;
}
The recursive part goes into AddChildren. I convert the path into a List<string> to be able to remove the added part. AddChildren assumes that the first item in path is the first child to be added.
static void AddChildren(PivotGroup group, List<string> path)
{
string key = path[0];
int index = group.ChildGroups.FindIndex(g => g.Key == key);
PivotGroup child;
if (index >= 0) { // A child with this key exists.
child = group.ChildGroups[index]; // Select this existing child.
} else { // This key is missing. Add a new child.
child = new PivotGroup { Key = key };
group.ChildGroups.Add(child);
}
if (path.Count > 1) {
path.RemoveAt(0); // Remove the added child key and add the rest recursively.
AddChildren(child, path);
}
}
We add children by walking down the tree and adding new children if necessary.
This prints the tree recursively:
private static void PrintTree(PivotGroup group, int level)
{
Console.WriteLine(new String(' ', 2 * level) + group.Key);
foreach (PivotGroup child in group.ChildGroups) {
PrintTree(child, level + 1);
}
}
string[] paths = new string[] {
"ROOT!ZZZ!AAA!EEE!15712",
...
};
PivotGroup root = BuildTree(paths);
PrintTree(root, 0);
Console.ReadKey();
We could also use a loop instead of doing a recursion, since we add one branch at a time:
static PivotGroup BuildTree(string[] paths)
{
var root = new PivotGroup { Key = "ROOT" };
foreach (string path in paths) {
PivotGroup group = root;
string[] pathElements = path.Split('!');
for (int i = 1; i < pathElements.Length; i++) { // Element [0] is ROOT, we skip it.
string key = pathElements[i];
int index = group.ChildGroups.FindIndex(g => g.Key == key);
PivotGroup child;
if (index >= 0) { // A child with this key exists.
child = group.ChildGroups[index]; // Select this existing child.
} else { // This key is missing. Add a new child.
child = new PivotGroup { Key = key };
group.ChildGroups.Add(child);
}
group = child;
}
}
return root;
}
List<T>.FindIndex is inefficient for large lists. If you have large data sets and the order does not matter, switch to Dictionary<string, PivotGroup>. If you need the data to be sorted, use SortedDictionary<string, PivotGroup>.
Here is some simple recursive code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] paths = new string[] {
"ROOT!ZZZ!AAA!EEE!15712",
"ROOT!ZZZ!AAA!EEE!15722",
"ROOT!ZZZ!AAA!EEE!13891"};
List<List<string>> inputData = paths.Select(x => x.Split(new char[] {'!'}).ToList()).ToList();
Node root = new Node();
Node.ParseTree(root, inputData);
}
}
public class Node
{
public string name { get; set; }
public List<Node> children { get; set; }
public static void ParseTree(Node parent, List<List<string>> inputData)
{
parent.name = inputData.First().FirstOrDefault();
var groups = inputData.Select(x => x.Skip(1)).GroupBy(x => x.Take(1).FirstOrDefault());
foreach (var group in groups)
{
if (group.Key != null)
{
if (parent.children == null) parent.children = new List<Node>();
Node newNode = new Node();
parent.children.Add(newNode);
ParseTree(newNode, group.Select(x => x.Select(y => y).ToList()).ToList());
}
}
}
}
}
I am working on a multi-level marketing (binary) which looks like this:
(but the binary tree is not required to be perfect. A node can have 0-2 child)
My problem is the data that I fetch from the database is flat list.
Notice that I am using hierarchyid (sql server 2014)
Basically the TextNode column is like a breadcrumb.
every slash / represents a level.
If I have TextNode of /1/ as root. then every node that starts with /1/ belongs to that root which are /1/, /1/1/ and /1/1/1/ (the root node is included which will be the level 0)
I've tried the accepted answer in this question but its not working.
How can I transform the flatlist to a Binary Tree so that I can easily traverse and display it on a screen?
Im using C#, ASP MVC 5, SQL Server 2014 if it matters.
I implement exactly this code According to Alex implementation but as is mentioned in some case it didn't work correctly .. have a look to my Image and my code (which copied from Alex post) [data in the database are correct but in tree view seems some problems ]
public class Row : IRow<string>
{
public string TextNode { get; }
public string Value { get; }
public long Id { get; }
public string FIN { get; }
public Row(string textNode, string userName, long id, string fin)
{
FIN = fin;
Id = id;
TextNode = textNode;
Value = userName;
}
}
public interface IRow<out T>
{
string TextNode { get; }
long Id { get; }
string FIN { get; }
T Value { get; }
}
public class TreeNode<T>
{
private struct NodeDescriptor
{
public int Level { get; }
public int ParentIndex { get; }
public NodeDescriptor(IRow<T> row)
{
var split = row.TextNode.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
Level = split.Length;
ParentIndex = split.Length > 1 ? int.Parse(split[split.Length - 2]) - 1 : 0;
}
}
public T title { get; }
public long Id { get; }
public string FIN { get; }
public List<TreeNode<T>> children { get; }
private TreeNode(T value, long id, string fin)
{
Id = id;
FIN = fin;
title = value;
children = new List<TreeNode<T>>();
}
public static TreeNode<T> Parse(IReadOnlyList<IRow<T>> rows)
{
if (rows.Count == 0)
return null;
var result = new TreeNode<T>(rows[0].Value, rows[0].Id, rows[0].FIN);
FillParents(new[] { result }, rows, 1, 1);
return result;
}
private static void FillParents(IList<TreeNode<T>> parents, IReadOnlyList<IRow<T>> rows, int index, int currentLevel)
{
var result = new List<TreeNode<T>>();
for (int i = index; i < rows.Count; i++)
{
var descriptor = new NodeDescriptor(rows[i]);
if (descriptor.Level != currentLevel)
{
FillParents(result, rows, i, descriptor.Level);
return;
}
var treeNode = new TreeNode<T>(rows[i].Value, rows[i].Id, rows[i].FIN);
parents[descriptor.ParentIndex].children.Add(treeNode);
result.Add(treeNode);
}
}
}
g
this is also my JSON output for more information :
{"title":"Earth","Id":32,"FIN":"FIN","children":[{"title":"Europe","Id":33,"FIN":"FIN001","children":[{"title":"France","Id":35,"FIN":"FIN001001","children":[{"title":"Paris","Id":36,"FIN":"FIN001001001","children":[]},{"title":"Brasilia","Id":41,"FIN":"FIN002001001","children":[]},{"title":"Bahia","Id":42,"FIN":"FIN002001002","children":[]}]},{"title":"Spain","Id":38,"FIN":"FIN001002","children":[{"title":"Madrid","Id":37,"FIN":"FIN001002001","children":[{"title":"Salvador","Id":43,"FIN":"FIN002001002001","children":[]}]}]},{"title":"Italy","Id":45,"FIN":"FIN001003","children":[]},{"title":"Germany","Id":48,"FIN":"FIN001004","children":[]},{"title":"test","Id":10049,"FIN":"FIN001005","children":[]}]},{"title":"South America","Id":34,"FIN":"FIN002","children":[{"title":"Brazil","Id":40,"FIN":"FIN002001","children":[{"title":"Morano","Id":47,"FIN":"FIN001003001","children":[]}]}]},{"title":"Antarctica","Id":39,"FIN":"FIN003","children":[{"title":"McMurdo Station","Id":44,"FIN":"FIN003001","children":[]}]}]}
Here is a very simple implementation (assuming that Nodes are in the right order), which may be enhanced in multiple ways
public interface IRow<out T>
{
string TextNode { get; }
T Value { get; }
}
public class TreeNode<T>
{
private struct NodeDescriptor
{
public int Level { get; }
public int ParentIndex { get; }
public NodeDescriptor(IRow<T> row)
{
var split = row.TextNode.Split(new [] {"/"}, StringSplitOptions.RemoveEmptyEntries);
Level = split.Length;
ParentIndex = split.Length > 1 ? int.Parse(split[split.Length - 2]) - 1 : 0;
}
}
public T Value { get; }
public List<TreeNode<T>> Descendants { get; }
private TreeNode(T value)
{
Value = value;
Descendants = new List<TreeNode<T>>();
}
public static TreeNode<T> Parse(IReadOnlyList<IRow<T>> rows)
{
if (rows.Count == 0)
return null;
var result = new TreeNode<T>(rows[0].Value);
FillParents(new[] {result}, rows, 1, 1);
return result;
}
private static void FillParents(IList<TreeNode<T>> parents, IReadOnlyList<IRow<T>> rows, int index, int currentLevel)
{
var result = new List<TreeNode<T>>();
for (int i = index; i < rows.Count; i++)
{
var descriptor = new NodeDescriptor(rows[i]);
if (descriptor.Level != currentLevel)
{
FillParents(result, rows, i, descriptor.Level);
return;
}
var treeNode = new TreeNode<T>(rows[i].Value);
parents[descriptor.ParentIndex].Descendants.Add(treeNode);
result.Add(treeNode);
}
}
}
Sample usage:
public class Row : IRow<string>
{
public string TextNode { get; }
public string Value { get; }
public Row(string textNode, string userName)
{
TextNode = textNode;
Value = userName;
}
}
class Program
{
static void Main(string[] args)
{
IRow<string>[] rows =
{
new Row("/", "Ahmed"),
new Row("/1/", "Saeed"),
new Row("/2/", "Amjid"),
new Row("/1/1/", "Noura"),
new Row("/2/1/", "Noura01"),
new Row("/2/2/", "Reem01"),
new Row("/1/1/1", "Under_noura")
};
var tree = TreeNode<string>.Parse(rows);
PrintTree(tree);
}
private static void PrintTree<T>(TreeNode<T> tree, int level = 0)
{
string prefix = new string('-', level*2);
Console.WriteLine("{0}{1}", prefix, tree.Value);
foreach (var node in tree.Descendants)
{
PrintTree(node, level + 1);
}
}
}
I have this LINQ statement that tries to set the 1st element in the collection of string[]. But it doesn't work.
Below is the LINQ statement.
docSpcItem.Where(x => x.DocID == 2146943)
.FirstOrDefault()
.FinishingOptionsDesc[0] = "new value";
public string[] FinishingOptionsDesc
{
get
{
if (this._FinishingOptionsDesc != null)
{
return (string[])this._FinishingOptionsDesc.ToArray(typeof(string));
}
return null;
}
set { this._FinishingOptionsDesc = new ArrayList(value); }
}
What's wrong with my LINQ statement above?
Couple of things.. There are some problems with your get and set. I would just use auto properties like this..
public class DocSpcItem
{
public string[] FinishingOptionsDesc { get; set; }
public int DocID { get; set; }
}
Next for your linq statement, depending on the presence of an item with an id of 2146943 you might be setting a new version of the object rather than the one you intended. This should work..
[TestMethod]
public void Linq()
{
var items = new List<DocSpcItem>();
//2146943
for (var i = 2146930; i <= 2146950; i++)
{
items.Add(new DocSpcItem()
{ DocID = i
, FinishingOptionsDesc = new string[]
{ i.ToString() }
}
);
}
var item = items.FirstOrDefault(i => i.DocID == 2146943);
if (item != null)
{
item.FinishingOptionsDesc = new string[]{"The New Value"};
}
}
and
public class DocSpcItem
{
public string[] FinishingOptionsDesc { get; set; }
public int DocID { get; set; }
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
linq to sql recursive query
I got stuck with having to build a Recursive select via LINQ for the self referencing table.
I use this class:
public class DivisionHierarchy
{
public Division Division { get; set; }
public IEnumerable<DivisionHierarchy> Divisions { get; set; }
}
and I created this function but somehow it is infinite.
public IEnumerable<DivisionHierarchy> GetDivisionHierarchy(IEnumerable<Division> allDivisions, Division parentDivision)
{
Guid? parentDivisionId = null;
if (parentDivision != null)
parentDivisionId = parentDivision.DivisionID;
var childDivisions = allDivisions.Where(e => e.DivisionID == parentDivisionId);
Collection<DivisionHierarchy> hierarchy = new Collection<DivisionHierarchy>();
foreach (var div in childDivisions)
hierarchy.Add(new DivisionHierarchy() { Division = div, Divisions = GetDivisionHierarchy(allDivisions, div) });
return hierarchy;
}
Any clue where I can start?
Thank you!
P.S. Are there any others ways to do it?
UPDATES based on http://www.scip.be/index.php?Page=ArticlesNET18#AsHierarchy
I found my errors.
There are 2 things to implement:
1. The root node should be created under the database.
I changed code a little bit.
Guid divisionID = Guid.Parse("5b487b3d-e9be-413f-b611-2fd7491e0d0d"); // Hardcoded somehow
var rootDivision = db.Divisions.Where(i => i.ID == divisionID).FirstOrDefault();
var divisionHierarchy = GetDivisionHierarchy(db.Divisions.AsEnumerable(), rootDivision);
...
public IEnumerable<DivisionHierarchy> GetDivisionHierarchy(IEnumerable<Division> allDivisions, Division parentDivision)
{
Guid? parentDivisionId = null;
if (parentDivision != null)
parentDivisionId = parentDivision.ID;
var childDivisions = allDivisions.Where(division => division.DivisionID == parentDivisionId);
Collection<DivisionHierarchy> hierarchy = new Collection<DivisionHierarchy>();
foreach (var div in childDivisions)
{
DivisionHierarchy divisionHierarchy = new DivisionHierarchy();
divisionHierarchy.Division = div;
divisionHierarchy.Divisions = GetDivisionHierarchy(allDivisions, div);
hierarchy.Add(divisionHierarchy);
}
return hierarchy;
}
I would load the divisions in an non-recursive way and then set up the recursive relations in code. Here is an example, which does this in a lazy way
public class Division
{
public int ID { get; set; }
public int DivisionID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
private static List<Division> _divisions;
public static List<Division> Divisions
{
get
{
if (_divisions == null) {
LoadAndSetUpDivisionsHierarchy();
}
return _divisions;
}
}
private static Dictionary<int, Division> _divisionsByID;
public static Dictionary<int, Division> DivisionsByID
{
get
{
if (_divisionsByID == null) {
LoadAndSetUpDivisionsHierarchy();
}
return _divisionsByID;
}
}
private static Division _root;
public static Division Root
{
get
{
if (_root == null) {
LoadAndSetUpDivisionsHierarchy();
}
return _root;
}
}
private Division _parentDivision;
public Division ParentDivision
{
get
{
if (_parentDivision == null && DivisionID != 0) {
_parentDivision = DivisionsByID[DivisionID];
}
return _parentDivision;
}
}
private List<Division> _subDivisions = new List<Division>();
public List<Division> SubDivisions
{
get { return _subDivisions; }
}
private static void LoadAndSetUpDivisionsHierarchyHierarchy()
{
// Load the divisions in a non-recursive way using LINQ
// (details not shown here).
_divisions = LoadDivisions();
// Add the divisions in a dictionary by id
_divisionsByID = new Dictionary<int, Division>(_divisions.Count);
foreach (Division division in _divisions) {
_divisionsByID.Add(division.ID, division);
}
// Define sub-divisions and root division
foreach (Division division in _divisions) {
if (division.DivisionID == 0) {
_root = division;
} else if (division.ParentDivision != null) {
division.ParentDivision.SubDivisions.Add(division);
}
}
}
private static List<Division> LoadDivisions()
{
throw new NotImplementedException();
}
}