Find tree item in c# - c#

I have a page in which a user can select a child of a child from a tree hierarchy in Xamarin Forms. After saving, once a user clicks the edit button, I need to loop over all the items to set the user's selected value again
For example:
public class A
{
public string Id {get;set;}
public string Name {get;set;}
public List<A> Items{get;set;}
}
In the VM, I have a method to initialize object A1 of type A. I need to loop over all the children of A to match a value of A to that of the selected Id
private A GetA(string id, List<A> items)
{
foreach (var a in items)
{
if (a.Id == id)
{
return a;
}
else
{
if (a.Items.Count > 0)
{
return GetA(id, a.Items);
}
}
}
return null;
}
So far, I wrote a recursive function that only loops on the first child of every A. Thus, can anyone provide me with a better solution?

The problem is, that you are not further iterating over the other items in the list, when a.Id != id and a.Items.Count > 0. You should instead save the result of the recursive GetA and only if it's not null return it, otherwise keep looping. Otherwise you would just loop until the first branch and then recursively only ever search all the first branches, but not any other ones.
private A GetA(string id, List<A> items)
{
foreach (var a in items)
{
if (a.Id == id)
{
return a;
}
// You could also remove this if and just call GetA directly,
// since GetA(id, a.Items) with an empty list,
// will always return null
if (a.Items.Count > 0)
{
var innerA = GetA(id, a.Items);
if (innerA != null) {
return GetA(id, a.Items);
}
}
}
return null;
}

Related

Be able to show the next value in a Linked List by clicking a button

This is probably a simple task however I am unable to solve.
So currently I have set up a form which contains a textbox and a button and I want to be able to click the button and the first value within the LinkedList will show up in the textbox. If I click the button again then the next value will show up etc.
I currently go it so that the first value will show up but then I am unable to proceed to the next value.
This is the code I have currently:
public class Node
{
public string data;
public Node next;
public Node(string newData)
{
data = newData;
next = null;
}
public void AddEnd(string data)
{
if (next == null)
{
next = new Node(data);
}
else
{
next.AddEnd(data);
}
}
}
public class myList
{
public void AddEnd(string data)
{
if (headnode == null)
{
headnode = new Node(data);
}
else
{
headnode.AddEnd(data);
}
}
public string getFirst() // this gets the first value within the list and returns it
{
if (headnode == null)
{
throw new Exception("List is empty");
}
Node node = headnode;
while (node.next != null)
{
node = node.next;
}
return node.data;
}
I also tried using this:
public class NavigationList<T> : List<T>
{
private int _currentIndex = -1;
public int CurrentIndex
{
get
{
if (_currentIndex == Count)
_currentIndex = 0;
else if (_currentIndex > Count - 1)
_currentIndex = Count - 1;
else if (_currentIndex < 0)
_currentIndex = 0;
return _currentIndex;
}
set { _currentIndex = value; }
}
public T MoveNext
{
get { _currentIndex++; return this[CurrentIndex]; }
}
public T Current
{
get { return this[CurrentIndex]; }
}
}
However, I am not really familiar with something like this so I wasn't sure on how to use it.
So you have a sequence of items, and the only thing that you want, is to get the first item, and once you've got an item, every time your ask for it, you want the next item, until there are no more items left.
In .NET this is called an IEnumerable, or if you know what kind of items are in your sequence, for instance items of MyClass, it is called an IEnumerable<MyClass>. In your case you need an IEnumerable<string>.
Luckily .NET is loaded with classes that implement IEnumerable. Two of the most used ones are array and list. You seldom have to create an enumerable class yourself, re-use the existing ones and enumerate over it.
List<string> myData = ... // fill this list somehow.
IEnumerator<string> myEnumerator = null // we are not enumerating yet.
string GetNextItemToDisplay()
{ // returns null if there are no more items to display
// if we haven't started yet, get the enumerator:
if (this.myEnumerator == null) this.myEnumerator = this.myData.GetEnumerator();
// get the next element (or if we haven't fetched anything yet: get the first element
// for this we use MoveNext. This returns false if there is no next element
while (this.myEnumerator.MoveNext())
{
// There is a next string. It is in Current:
string nextString = enumerator.Current();
return nextString;
}
// if here: no strings left. return null:
return null;
}
This looks like a lot of code, but if you remove the comments it is in fact just a few lines of code:
string GetNextItemToDisplay()
{
if (this.myEnumerator == null) this.myEnumerator = this.myData.GetEnumerator();
while (this.myEnumerator.MoveNext())
return enumerator.Current();
return null;
}
Your ButtonClick event handler:
void OnButtonClick(object sender, eventArgs e)
{
string nextItemToDisplay = this.GetNextItemToDisplay();
if (nextItemToDisplay != null)
this.Display(nextItemToDisplay);
else
this.DisplayNoMoreItems():
}
If you want to start over again with the first element, for instance after changing the List
void RestartEnumeration()
{
this.myEnumerator = null;
}

How to read large text files and keep tracking of the information of previous lines using C#?

(This problem is a adaptation of a real life scenario, I reduced the problem so it is easy to understand, otherwise this question would be 10000 lines long)
I have a pipe delimited text file that looks like this (the header is not in the file):
Id|TotalAmount|Reference
1|10000
2|50000
3|5000|1
4|5000|1
5|10000|2
6|10000|2
7|500|9
8|500|9
9|1000
The reference is optional and is the Id of another entry in this text file. The entries that have a reference, are considered "children" of that reference, and the reference is their parent. I need to validate each parent in the file, and the validation is that the sum of TotalAmount of it's children should be equal to the parent's total amount. The parents can be either first or before their children in the file, like the entry with Id 9, that comes after it's children
In the provided file, the entry with Id 1 is valid, because the sum of the total amount of it's children (Ids 3 and 4) is 10000 and the entry with Id 2 is invalid, because the sum of it's children (Ids 5 and 6) is 20000.
For a small file like this, I could just parse everything to objects like this (pseudo code, I don't have a way to run it now):
class Entry
{
public int Id { get; set; }
public int TotalAmout { get; set; }
public int Reference { get; set; }
}
class Validator
{
public void Validate()
{
List<Entry> entries = GetEntriesFromFile(#"C:\entries.txt");
foreach (var entry in entries)
{
var children = entries.Where(e => e.Reference == entry.Id).ToList();
if (children.Count > 0)
{
var sum = children.Sum(e => e.TotalAmout);
if (sum == entry.TotalAmout)
{
Console.WriteLine("Entry with Id {0} is valid", entry.Id);
}
else
{
Console.WriteLine("Entry with Id {0} is INVALID", entry.Id);
}
}
else
{
Console.WriteLine("Entry with Id {0} is valid", entry.Id);
}
}
}
public List<Entry> GetEntriesFromFile(string file)
{
var entries = new List<Entry>();
using (var r = new StreamReader(file))
{
while (!r.EndOfStream)
{
var line = r.ReadLine();
var splited = line.Split('|');
var entry = new Entry();
entry.Id = int.Parse(splited[0]);
entry.TotalAmout = int.Parse(splited[1]);
if (splited.Length == 3)
{
entry.Reference = int.Parse(splited[2]);
}
entries.Add(entry);
}
}
return entries;
}
}
The problem is that I am dealing with large files (10 GB), and that would load way to many objects in memory.
Performance itself is NOT a concern here. I know that I could use dictionaries instead of the Where() method for example. My only problem now is performing the validation without loading everything to memory, and I don't have any idea how to do it, because a entry at the bottom of the file may have a reference to the entry at the top, so I need to keep track of everything.
So my question is: it is possible to keep track of each line in a text file without loading it's information into memory?
Since performance is not an issue here, I would approach this in the following way:
First, I would sort the file so all the parents go right before their children. There are classical methods for sorting huge external data, see https://en.wikipedia.org/wiki/External_sorting
After that, the task becomes pretty trivial: read a parent data, remember it, read and sum children data one by one, compare, repeat.
All you really need to keep in memory is the expected total for each non-child entity, and the running sum of the child totals for each parent entity. Everything else you can throw out, and if you use the File.ReadLines API, you can stream over the file and 'forget' each line once you've processed it. Since the lines are read on demand, you don't have to keep the entire file in memory.
public class Entry
{
public int Id { get; set; }
public int TotalAmount { get; set; }
public int? Reference { get; set; }
}
public static class EntryValidator
{
public static void Validate(string file)
{
var entries = GetEntriesFromFile(file);
var childAmounts = new Dictionary<int, int>();
var nonChildAmounts = new Dictionary<int, int>();
foreach (var e in entries)
{
if (e.Reference is int p)
childAmounts.AddOrUpdate(p, e.TotalAmount, (_, n) => n + e.TotalAmount);
else
nonChildAmounts[e.Id] = e.TotalAmount;
}
foreach (var id in nonChildAmounts.Keys)
{
var expectedTotal = nonChildAmounts[id];
if (childAmounts.TryGetValue(id, out var childTotal) &&
childTotal != expectedTotal)
{
Console.WriteLine($"Entry with Id {id} is INVALID");
}
else
{
Console.WriteLine($"Entry with Id {id} is valid");
}
}
}
private static IEnumerable<Entry> GetEntriesFromFile(string file)
{
foreach (var line in File.ReadLines(file))
yield return GetEntryFromLine(line);
}
private static Entry GetEntryFromLine(string line)
{
var parts = line.Split('|');
var entry = new Entry
{
Id = int.Parse(parts[0]),
TotalAmount = int.Parse(parts[1])
};
if (parts.Length == 3)
entry.Reference = int.Parse(parts[2]);
return entry;
}
}
This uses a nifty extension method for IDictionary<K, V>:
public static class DictionaryExtensions
{
public static TValue AddOrUpdate<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key,
TValue addValue,
Func<TKey, TValue, TValue> updateCallback)
{
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
if (updateCallback == null)
throw new ArgumentNullException(nameof(updateCallback));
if (dictionary.TryGetValue(key, out var value))
value = updateCallback(key, value);
else
value = addValue;
dictionary[key] = value;
return value;
}
}

How to remove items from a List of Structs that exist in another List

public struct RegistryApp
{
public string VendorName;
public string Name;
public string Version;
}
I Have two List<RegistryApp> which hold all Applications currently installed on the Windows box. Why two? Well I have one List to hold all x86 Applications and one to hold all x64 Applications.
List<RegistryApp> x64Apps64List = new List<RegistryApp>();
List<RegistryApp> x64Apps32List = new List<RegistryApp>();
Once those two are populated with their appropriate data which was retrieved from the registry, I try the following to make sure there are no duplicates. This worked decently on List<string> but not working with List<RegistryApp>.
List<RegistryApp> ListOfAllAppsInstalled = new List<RegistryApp>();
IEnumerable<RegistryApp> x86Apps = x64Apps32List.Except(x64Apps64List);
IEnumerable<RegistryApp> x64Apps = x64Apps64List.Except(x64Apps32List);
foreach (RegistryApp regitem in x86Apps)
{
if ((regitem.Name != null) &&
(regitem.Name.Length > 2) &&
(regitem.Name != ""))
{
ListOfAllAppsInstalled.Add(regitem);
}
}
foreach (RegistryApp regitem in x64Apps)
{
if ((regitem.Name != null) &&
(regitem.Name.Length > 2) &&
(regitem.Name != ""))
{
ListOfAllAppsInstalled.Add(regitem);
}
}
Any way to pull this off?
EDITED
To remove items from a List of Structs that exist in another List you can see the solution provided by Cuong Le Here :
https://stackoverflow.com/a/12784937/1507182
By using the Distinct parameterless extension method on the List type, we can remove those duplicate elements.
Then, we can optionally invoke the ToList extension to get an actual List with the duplicates removed.
static void Main()
{
// List with duplicate elements.
List<int> mylist = new List<int>();
mylist.Add(1);
mylist.Add(2);
mylist.Add(3);
mylist.Add(3);
mylist.Add(4);
mylist.Add(4);
mylist.Add(4);
foreach (int value in mylist)
{
Console.WriteLine("Before: {0}", value);
}
// Get distinct elements and convert into a list again.
List<int> distinct = mylist.Distinct().ToList();
foreach (int value in distinct)
{
Console.WriteLine("After: {0}", value);
}
}
If my answer has solved your problem click Accept as solution button, doing it will help others know the solution.
For Execpt to work the thing you are using it on must be compareable. To make it work for your custom struct you will need to do one of two things, either override GetHashCode and Equals to be able to use Execpt with your struct:
public struct RegistryApp
{
public string VendorName;
public string Name;
public string Version;
public override bool Equals(object obj)
{
if (!(obj is MyStruct))
return false;
RegistryApp ra = (RegistryApp) obj;
return ra.VendorName == this.VendorName &&
ra.Name == this.Name &&
ra.Version == this.Version;
}
public override int GetHashCode()
{
return VendorName.GetHashCode() ^ Name.GetHashCode() ^ Version.GetHashCode();
}
}
or use the overload of Execpt that allows you to pass in your own comparer and pass that in. See the MSDN for an example

How to filter a recursive object?

In my current project, a method I don't control sends me an object of this type:
public class SampleClass
{
public SampleClass();
public int ID { get; set; }
public List<SampleClass> Items { get; set; }
public string Name { get; set; }
public SampleType Type { get; set; }
}
public enum SampleType
{
type1,
type2,
type3
}
I display those data in a TreeView, but I would like to display only the path ending with SampleClass objects having their Type property set to type3, no matter the depth of this leaf.
I have absolutely no clue on how to do that, can someone help me ?
Thanks in advance !
Edit
To explain the problem I meet with the solutions proposed by Shahrooz Jefri and dasblinkenlight, here is a picture. The left column is the original data, without filtering, and the right one is the data filtered. Both methods provide the same result.
In red is the problem.
Use this Filter method:
public void Filter(List<SampleClass> items)
{
if (items != null)
{
List<SampleClass> itemsToRemove = new List<SampleClass>();
foreach (SampleClass item in items)
{
Filter(item.Items);
if (item.Items == null || item.Items.Count == 0)
if (item.Type != SampleType.type3)
itemsToRemove.Add(item);
}
foreach (SampleClass item in itemsToRemove)
{
items.Remove(item);
}
}
}
In addition to initially determining which items to show, if the datasize is substantial and you expect users to frequently collapse and expand sections then filtering after every click my result in slow ui response.
Consider the Decorator pattern or some other way of tagging each node with relevant info so that the filtering is not required after every click.
Try this approach:
static bool ShouldKeep(SampleClass item) {
return (item.Type == SampleType.type3 && item.Items.Count == 0)
|| item.Items.Any(ShouldKeep);
}
static SampleClass Filter(SampleClass item) {
if (!ShouldKeep(item)) return null;
return new SampleClass {
Id = item.Id
, Name = item.Name
, Type = item.Type
, Items = item.Items.Where(ShouldKeep).Select(x=>Filter(x)).ToList()
};
}
The above code assumes that Items of leaves are empty lists, rather than nulls.

Get to next element in List

If i have a list of objects and i want to move to the next node with each function call (ie create a "GetNextNode" how would i go about doing this? Right now i have one method which will get the first node of my List and set the currentObj to it and return it (leaving previous node still at null) a flag indicates that we're not dealing with the first node in the list anymore. then i move forward and i want to iterate through the list (using foreach i guess?) to one node past my currentObj. Here is my code:
List<Employee> ListOfEmployees = new List<Employee>();
Employee currEmployeeObj = null;
Employee prevEmployeeObj = null;
foreach (Employee employee in ListOfEmployees)
{
//how do i keep track of the previous and current employee in here?
}
return (currEmployeeObj);
}
I hate to sound like a dinosaur, but since you're implementing with a List anyway, why not iterate over it with for instead of foreach? Integers are really useful for comparisons like i == j + 1
Looks like you really are re-inventing an enumerator:
public IEnumerator<Employee> GetEmployees()
{
foreach (Employee employee in ListOfEmployees)
{
//custom processing here
yield return employee;
}
}
Usage:
var myEnumerator = foo.GetEmployees();
while(myEnumerator.MoveNext())
{
var someEmployee = myEnumerator.Current;
//do something
}
Just as an update here is the full class implementation so you can verify it compiles and works..
public class Foo
{
List<Employee> ListOfEmployees = new List<Employee>();
public Foo()
{
ListOfEmployees.Add(new Employee());
}
public IEnumerator<Employee> GetEmployees()
{
foreach (Employee employee in ListOfEmployees)
yield return employee;
}
}
(As an academic exercise, the other answers are probably more appropriate here: )
You could create an extension method like so:
public static IEnumerable<Tuple<T, T>> ToPairs<T>(this IEnumerable<T> enumerable)
{
using (var enumerator = enumerable.GetEnumerator())
{
if (enumerator.MoveNext())
{
var previous = enumerator.Current;
while (enumerator.MoveNext())
{
var current = enumerator.Current;
yield return new Tuple<T, T>(previous, current);
previous = current;
}
}
}
}
To return you a tuple containing pairs of elements.
Which would be used like:
foreach (var pair in ListOfEmployees.ToPairs())
{
Employee prevEmployee = pair.Item1;
Employee currEmployeeObj = pair.Item2;
}
http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.aspx
The link above this line of text has what will work to solve my issue.
Thanks all for the responses and help! Upvoted those who tried to help and had something to offer

Categories

Resources