Populate a tree from a List of string arrays - c#

I have list of arrays:
List<HeaderItem> _headerItems = new List<HeaderItem>();
class HeaderItem
{
private string[] _headers = new string[6];
public string this[int index]
{
get
{
return _headers[index];
}
set
{
_headers[index] = value;
}
}
}
Each of the 6 items in the array represent a level in the hierarchy. If all items matched in array position 0 then a single root level node would exist.
So,
A,B,C
A,B,D
B,C,D
B,D,E
would produce:
A
....B
........C
........D
B
....C
........D
....D
........E
etc....
Currently my solution is pretty hacked up and although it works I am trying to come up with a "cool" way of doing it.

You can achieve that by calling a print method recursively providing it with the subset of items to be printed and depth of the tree printed so far. I amended your class to contain a Length property so that the caller does not have to always assume it is 6. Also I added a constructor to make my initialization easy. Probably it wouldn't make sense in your code.
public class HeaderItem
{
public HeaderItem(string headers)
{
_headers = headers.ToCharArray().Select(x => x.ToString()).ToArray();
}
private string[] _headers = new string[6];
public int Length
{
get { return _headers.Length; }
}
//...
}
This is the print method. See how it does grouping and then calls itself recursively:
private static void PrintHeaders(IEnumerable<HeaderItem> headerItems, int depth = 0)
{
var result =
headerItems.Where(h => h.Length > depth)
.GroupBy(h => h[depth], h => h,
(k, g) => new {Key = k, Items = g})
.OrderBy(g => g.Key);
foreach (var pair in result)
{
Console.Write(new string('.', depth)); // change here to add more dots
Console.WriteLine(pair.Key);
PrintHeaders(pair.Items, depth + 1);
}
}
And this is how you can begin calling it:
PrintHeaders(_headerItems);
For testing, this is my Main method:
static void Main(string[] args)
{
_headerItems.Add(new HeaderItem("abc"));
_headerItems.Add(new HeaderItem("abd"));
_headerItems.Add(new HeaderItem("acd"));
_headerItems.Add(new HeaderItem("ace"));
_headerItems.Add(new HeaderItem("bce"));
_headerItems.Add(new HeaderItem("bcd"));
_headerItems.Add(new HeaderItem("bef"));
PrintHeaders(_headerItems);
Console.ReadLine();
}
And this is the result:
a
.b
..c
..d
.c
..d
..e
b
.c
..d
..e
.e
..f

When ever making tree always create a list of entity within that entity like this
class HeaderItem
{
private string[] _headers = new string[6];
private List<HeaderItem> _items;
public string this[int index]
{
get
{
return _headers[index];
}
set
{
_headers[index] = value;
}
}
public List<HeaderItem> Items
{
get
{
if (_items == null)
_items = new List<HeaderItem>();
return _items;
}
}
}

Related

C# custom add in a List

I have the following list of strings :
var files = new List<string> {"file0","file1","file2","file3" };
I would like to be able to add new files to this list, but if the inserted file is present in the list, I would like to insert custom value that will respect the following format $"{StringToBeInserted}"("{SomeCounter}
For instance : try to add "file0" and "file0" is already I would like to insert "file0(1)". If I try again to add "file0" ... I would like to insert with "file0(2)" and so on ... Also, I would like to provide a consistency, for instance if I delete "file0(1)" ... and try to add again "item0" ... I expect that "item0(1)" to be added. Can someone help me with a generic algorithm ?
I would use a HashSet<string> in this case:
var files = new HashSet<string> { "file0", "file1", "file2", "file3" };
string originalFile = "file0";
string file = originalFile;
int counter = 0;
while (!files.Add(file))
{
file = $"{originalFile}({++counter})";
}
If you have to use a list and the result should also be one, you can still use my set approach. Just initialize it with your list and the result list you'll get with files.ToList().
Well, you should create your own custom class for it, using the data structure you described and a simple class that includes a counter and an output method.
void Main()
{
var items = new ItemCountList();
items.AddItem("item0");
items.AddItem("item1");
items.AddItem("item2");
items.AddItem("item0");
items.ShowItems();
}
public class ItemCountList {
private List<SimpleItem> itemList;
public ItemCountList() {
itemList = new List<SimpleItem>();
}
public void DeleteItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null) {
item.Count--;
if (item.Count == 0)
itemList.Remove(item);
}
}
public void AddItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null)
item.Count++;
else
itemList.Add(new SimpleItem {
Value = value,
Count = 1
});
}
public void ShowItems() {
foreach (var a in itemList) {
Console.WriteLine(a.Value + "(" + a.Count + ")");
}
}
}
public class SimpleItem {
public int Count {get; set;}
public string Value {get; set;}
}

Interleave an array of email addresses avoiding items with same domain to be consecutive

I'm looking for an efficient way of sorting an array of email addresses to avoid items with the same domain to be consecutive, in C#.
Email addresses inside the array are already distinct and all of them are lower case.
Example:
Given an array with the following entries:
john.doe#domain1.com
jane_doe#domain1.com
patricksmith#domain2.com
erick.brown#domain3.com
I would like to obtain something similar to the following:
john.doe#domain1.com
patricksmith#domain2.com
jane_doe#domain1.com
erick.brown#domain3.com
With the help of an extension method (stolen from https://stackoverflow.com/a/27533369/172769), you can go like this:
List<string> emails = new List<string>();
emails.Add("john.doe#domain1.com");
emails.Add("jane_doe#domain1.com");
emails.Add("patricksmith#domain2.com");
emails.Add("erick.brown#domain3.com");
var q = emails.GroupBy(m => m.Split('#')[1]).Select(g => new List<string>(g)).Interleave();
The Interleave method is defined as:
public static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source )
{
var queues = source.Select(x => new Queue<T>(x)).ToList();
while (queues.Any(x => x.Any())) {
foreach (var queue in queues.Where(x => x.Any())) {
yield return queue.Dequeue();
}
}
}
So basically, we create groups based on the domain part of the email adresses, project (or Select) each group into a List<string>, and then "Interleave" those lists.
I have tested against your sample data, but more thorough testing might be needed to find edge cases.
DotNetFiddle snippet
Cheers
This will distribute them semi-evenly and attempt to avoid matching domains next to each other (although in certain lists that may be impossible). This answer will use OOP and Linq.
DotNetFiddle.Net Example
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var seed = new List<string>()
{
"1#a.com",
"2#a.com",
"3#a.com",
"4#a.com",
"5#a.com",
"6#a.com",
"7#a.com",
"8#a.com",
"9#a.com",
"10#a.com",
"1#b.com",
"2#b.com",
"3#b.com",
"1#c.com",
"4#b.com",
"2#c.com",
"3#c.com",
"4#c.com"
};
var work = seed
// Create a list of EmailAddress objects
.Select(s => new EmailAddress(s)) // s.ToLowerCase() ?
// Group the list by Domain
.GroupBy(s => s.Domain)
// Create a List<EmailAddressGroup>
.Select(g => new EmailAddressGroup(g))
.ToList();
var currentDomain = string.Empty;
while(work.Count > 0)
{
// this list should not be the same domain we just used
var noDups = work.Where(w => w.Domain != currentDomain);
// if none exist we are done, or it can't be solved
if (noDups.Count() == 0)
{
break;
}
// find the first group with the most items
var workGroup = noDups.First(w => w.Count() == noDups.Max(g => g.Count()));
// get the email address and remove it from the group list
var workItem = workGroup.Remove();
// if the group is empty remove it from *work*
if (workGroup.Count() == 0)
{
work.Remove(workGroup);
Console.WriteLine("removed: " + workGroup.Domain);
}
Console.WriteLine(workItem.FullEmail);
// last domain looked at.
currentDomain = workItem.Domain;
}
Console.WriteLine("Cannot disperse email addresses affectively, left overs:");
foreach(var workGroup in work)
{
while(workGroup.Count() > 0)
{
var item = workGroup.Remove();
Console.WriteLine(item.FullEmail);
}
}
}
public class EmailAddress
{
public EmailAddress(string emailAddress)
{
// Additional Email Address Validation
var result = emailAddress.Split(new char[] {'#'}, StringSplitOptions.RemoveEmptyEntries)
.ToList();
if (result.Count() != 2)
{
new ArgumentException("emailAddress");
}
this.FullEmail = emailAddress;
this.Name = result[0];
this.Domain = result[1];
}
public string Name { get; private set; }
public string Domain { get; private set; }
public string FullEmail { get; private set; }
}
public class EmailAddressGroup
{
private List<EmailAddress> _emails;
public EmailAddressGroup(IEnumerable<EmailAddress> emails)
{
this._emails = emails.ToList();
this.Domain = emails.First().Domain;
}
public int Count()
{
return _emails.Count();
}
public string Domain { get; private set; }
public EmailAddress Remove()
{
var result = _emails.First();
_emails.Remove(result);
return result;
}
}
}
Output:
1#a.com
1#b.com
2#a.com
1#c.com
3#a.com
2#b.com
4#a.com
2#c.com
5#a.com
3#b.com
6#a.com
3#c.com
7#a.com
removed: b.com
4#b.com
8#a.com
removed: c.com
4#c.com
9#a.com
Cannot disperse email addresses affectively, left overs:
10#a.com
Something like this will spread them equally, but you will have the problems (=consecutive elements) at the end of the new list...
var list = new List<string>();
list.Add("john.doe#domain1.com");
list.Add("jane_doe#domain1.com");
list.Add("patricksmith#domain2.com");
list.Add("erick.brown#domain3.com");
var x = list.GroupBy(content => content.Split('#')[1]);
var newlist = new List<string>();
bool addedSomething=true;
int i = 0;
while (addedSomething) {
addedSomething = false;
foreach (var grp in x) {
if (grp.Count() > i) {
newlist.Add(grp.ElementAt(i));
addedSomething = true;
}
}
i++;
}
Edit: Added a high level description :)
What this code does is group each element by the domain, sort the groups by size in descending order (largest group first), project the elements of each group into a stack, and pop them off of each stack (always pop the next element off the largest stack with a different domain). If there is only a single stack left, then its contents are yielded.
This should make sure that all domains distributed as evenly as possible.
MaxBy extension method from: https://stackoverflow.com/a/31560586/969962
private IEnumerable<string> GetNonConsecutiveEmails(List<string> list)
{
var emailAddresses = list.Distinct().Select(email => new EmailAddress { Email = email, Domain = email.Split('#')[1]}).ToArray();
var groups = emailAddresses
.GroupBy(addr => addr.Domain)
.Select (group => new { Domain = group.Key, EmailAddresses = new Stack<EmailAddress>(group)})
.ToList();
EmailAddress lastEmail = null;
while(groups.Any(g => g.EmailAddresses.Any()))
{
// Try and pick from the largest stack.
var stack = groups
.Where(g => (g.EmailAddresses.Any()) && (lastEmail == null ? true : lastEmail.Domain != g.Domain))
.MaxBy(g => g.EmailAddresses.Count);
// Null check to account for only 1 stack being left.
// If so, pop the elements off the remaining stack.
lastEmail = (stack ?? groups.First(g => g.EmailAddresses.Any())).EmailAddresses.Pop();
yield return lastEmail.Email;
}
}
class EmailAddress
{
public string Domain;
public string Email;
}
public static class Extensions
{
public static T MaxBy<T,U>(this IEnumerable<T> data, Func<T,U> f) where U:IComparable
{
return data.Aggregate((i1, i2) => f(i1).CompareTo(f(i2))>0 ? i1 : i2);
}
}
What I am trying to do here is to sort them first.
Then I re-arrange from a different end. I'm sure there're more efficient ways to do this but this is one easy way to do it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
String[] emails = { "john.doe#domain1.com", "jane_doe#domain1.com", "patricksmith#domain2.com", "erick.brown#domain3.com" };
var result = process(emails);
}
static String[] process(String[] emails)
{
String[] result = new String[emails.Length];
var comparer = new DomainComparer();
Array.Sort(emails, comparer);
for (int i = 0, j = emails.Length - 1, k = 0; i < j; i++, j--, k += 2)
{
if (i == j)
result[k] = emails[i];
else
{
result[k] = emails[i];
result[k + 1] = emails[j];
}
}
return result;
}
}
public class DomainComparer : IComparer<string>
{
public int Compare(string left, string right)
{
int at_pos = left.IndexOf('#');
var left_domain = left.Substring(at_pos, left.Length - at_pos);
at_pos = right.IndexOf('#');
var right_domain = right.Substring(at_pos, right.Length - at_pos);
return String.Compare(left_domain, right_domain);
}
}
}

Using getter/setter for an array

I'm trying to create a getter/setter for an array but I'm not sure how to write it. The class will have multiple properties that will need to be modified and accessed from another class. Here is an idea of what I'm trying to do:
class MyArrayClass {
private double[] myArray = {1.1, 2.2};
public double MyArray {
get { return myArray[index]; }
set { myArray[index] = value; }
}
}
class AnotherClass {
MyArrayClass mAC = new MyArrayClass();
mAC.MyArray[1] = 3.3;
}
Now that code doesn't work but I hope it expresses what I'm trying to do. I was able to achieve what I wanted using the below code (which does work) however it only works for one property within that class.
class MyArrayClass {
private double[] myArray = {1.1, 2.2};
public double this[int index] {
get { return myArray[index]; }
set { myArray[index] = value; }
}
}
class AnotherClass {
MyArrayClass mAC = new MyArrayClass();
mAC[1] = 3.3;
}
I'm also not sure how to define the values of different index positions without doing it multiple times, e.g.
mAC.MyArray[0] = 1.1;
mAC.MyArray[1] = 2.2;
As opposed to something such as:
mAC.MyArray[0, 1] = {1.1, 2.2};
Sorry if it's a mess but I hope it conveys what I'm trying to achieve.
Since you have several arrays, you need to expose several array-like indexable properties. Using your second MyArrayClass as an example, you can do it like this:
class MyTwoArrays {
private MyArrayClass array1 = ...;
private MyArrayClass array2 = ...;
public MyArrayClass Array1 {
get { return array1; }
}
public MyArrayClass Array2 {
get { return array2; }
}
}
Now you can use it like this:
MyTwoArrays two = new MyTwoArrays();
two.Array1[0] = 123.456;
two.Array2[0] = 789.432;
mAC.MyArray[0, 1] = {1.1, 2.2};
This is not the way it should be, it has to be done one by one. The way you're using would change it Two dimensional array, (somehow, it would be invalid, zero main elements and 1 element in each).
mAC.MyArray[0] = 1.1;
mAC.MyArray[1] = 2.2;
Is the valid way to set the arrays.
class MyArrayClass {
private double[] myArray = {1.1, 2.2};
public double this[int index] {
get { return myArray[index]; }
set { myArray[index] = value; }
}
}
class AnotherClass {
MyArrayClass mAC = new MyArrayClass();
mAC[1] = 3.3;
}
Code works because if the valid way of doing the process because only in this case, you're passing an actual index number to the array to return the value from. In the first code block, there was no Index number, and the return was an Array object.
If I understand correctly, you want your indexer to access 2 internal arrays representing heights and weights of animals.
class MyArrayClass {
private double[] heights = {1.1, 2.2};
private double[] weights = {1.1, 2.2};
public double[] this[int index] { //Or Tuple<double ,double>
get { return new double[] { heights[index], weights[index] }; }
set { heights[index] = value[0]; weights[index] = value[1]; }
}
}
If your intention is to access 2 numbers from 1 array, you can do that too.
class MyArrayClass {
private double[] myArray = {1.1, 2.2, 3.3, 4.4};
public double[] this[int index] {
get { return new double[] { myArray[index*2], myArray[index*2+1] }; }
set { myArray[index*2] = value[0]; myArray[index*2+1] = value[1]; }
}
}
This doesn't include checking the index or the argument (it can be null or have length < 2).
It might be more convenient in this case to have an Animal struct or something:
class MyArrayClass {
private Animal[] animals = {new Animal(1.1, 1.1), new Animal(2.2, 2.2)}
public Animal this[int index] {
get { return animals[index]; }
set { animals[index] = value; }
}
}

how can i get an access to the elements of array in the class

I have a problem which I don't know how to solve. I have a class. This class has two arrays. I would like to get access via properties. How can I do it? I tried to use indexers, but it is possible if I have only one array. Here what I want to do:
public class pointCollection
{
string[] myX;
double[] myY;
int maxArray;
int i;
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myX = new string[maxArray];
this.myY = new double[maxArray];
}
public string X //It is just simple variable
{
set { this.myX[i] = value; }
get { return this.myX[i]; }
}
public double Y //it's too
{
set { this.myY[i] = value; }
get { return this.myY[i]; }
}
}
With this code, my X and Y are only simple variables, but not arrays.
If I use indexers, I get access only to one array:
public string this[int i]
{
set { this.myX[i] = value; }
get { return this.myX[i]; }
}
But how can I get access to second array?
Or I can't use property in this case? And I need only use:
public string[] myX;
public double[] myY;
An example with Tuples.
public class pointCollection
{
Tuple<String,Double>[] myPoints;
int maxArray;
int i;
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myPoints = new Tuple<String,Double>[maxArray];
}
public Tuple<String,Double> this[int i]
{
set { this.myPoints[i] = value; }
get { return this.myPoints[i]; }
}
}
And to access the points you do...
pointCollection pc = new pointCollection(10);
// add some data
String x = pc[4].Item1; // the first entry in a tuple is accessed via the Item1 property
Double y = pc[4].Item2; // the second entry in a tuple is accessed via the Item2 property
If I got it right, you need some kind or read/write-only wrapper for arrays to be exposed as properties.
public class ReadWriteOnlyArray<T>{
private T[] _array;
public ReadWriteOnlyArray(T[] array){
this._array = array;
}
public T this[int i]{
get { return _array[i]; }
set { _array[i] = value; }
}
}
public class pointCollection
{
string[] myX;
double[] myY;
int maxArray;
public ReadWriteOnlyArray<string> X {get; private set;}
public ReadWriteOnlyArray<double> Y {get; private set;}
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myX = new string[maxArray];
this.myY = new double[maxArray];
X = new ReadWriteOnlyArray<string>(myX);
Y = new ReadWriteOnlyArray<double>(myY);
}
}
and usage
var c = new pointCollection(100);
c.X[10] = "hello world";
c.Y[20] = c.Y[30] + c.Y[40];
The closest you'll come without either changing your data structure or moving to methods is to make a property that returns each array, much like you did in your first code block, except without the [i].
Then, you do var x = instanceOfPointCollection.MyX[someI]; for example.

List inside object becomes empty again after exiting function block

I have an object 'ForValidation' that has List of int as a property,
and an object 'Validator' which has a Verify(IEnumerable ForValidation) method. Verify method adds numbers in ForValidation list property.
In main function, I have IEnumerable of Validator and IEnumerable of ForValidation
every time Verify(IEnumerable) exits, the list inside ForValidation is back at 0 count.
From my understanding, objects are reference types in C# and modifications from anywhere should reflect in the same object.
I tried running visual studio debugger line by line to check that list inside 'ForValidation' is in fact being added data and then disappears after Verify method.
public class ForValidation
{
private readonly object #lock = new object();
private readonly List<int> ExistenceChecks = new List<int>();
public IEnumerable<int> ExistsPlaces => ExistenceChecks;
public string CheckProperty { get; }
public ForValidation(string checkProperty )
{
CheckProperty = checkProperty ;
}
public void ConfirmExistence(int place)
{
lock (#lock)
{
ExistenceChecks.Add(place);
}
}
}
public class Validator
{
public int ValidatorNumber { get; }
private readonly Datasource somedatasource;
public Validator(int number, Datasource someds)
{
ValidatorNumber = number;
somedatasource = someds;
}
public void Verify(IEnumerable<ForValidation> forValidations)
{
ForValidation[] copy = forValidations.ToArray();
IEnumerable<string> checkProperties = from member in copy
select member.CheckProperty;
IEnumerable<CompareAgainst> existingMembers
= somedatasource.Filter(new CheckPropertiesFilter(checkProperties)).Execute();
foreach (ForValidation forValidation in copy)
{
if (existingMembers.FirstOrDefault(m => m.CheckProperty == forValidation.CheckProperty) != null)
{
forValidation.ConfirmExistence(ValidatorNumber);
}
}
int x = copy.Length;
//each forValidation.ExistsPlaces has items until this code block
}
}
main
{
private readonly IEnumerable<ForValidation> forValidations {...}
private readonly IEnumerable<Validator> validators {...}
foreach (Validator validator in validators)
{
validator.Verify(forValidations);
// each forValidation.ExistsPlaces count is 0 again in this block
}
}
IExpect every ForValidation items inside forValidations will have remembered items inside its IEnumerable ExistsPlaces property after each Verify method by validators but it becomes 0 count after each iteration of Verify method in the foreach loop
I cannot reproduce your problem. Here is my code.
public static void Main(string[] args)
{
var validators = new[] { new Validator(666), new Validator(667) };
var forValidations = new [] { new ForValidation("v1"), new ForValidation("v2") };
Console.WriteLine("Before Verify");
foreach (var fv in forValidations)
Console.WriteLine($"Object: {fv.CheckProperty} - count of places: {fv.ExistsPlaces.Count()}");
foreach (Validator validator in validators)
validator.Verify(forValidations);
Console.WriteLine("After Verify");
foreach (var fv in forValidations)
Console.WriteLine($"Object: {fv.CheckProperty} - count of places: {fv.ExistsPlaces.Count()}");
}
Result
Before Verify
Object: v1 - count of places: 0
Object: v2 - count of places: 0
After Verify
Object: v1 - count of places: 2
Object: v2 - count of places: 2
Classes:
public class ForValidation
{
private readonly object #lock = new object();
private readonly List<int> ExistenceChecks = new List<int>();
public IEnumerable<int> ExistsPlaces => ExistenceChecks;
public string CheckProperty { get; }
public ForValidation(string checkProperty)
{
CheckProperty = checkProperty;
}
public void ConfirmExistence(int place)
{
lock (#lock)
{
ExistenceChecks.Add(place);
}
}
}
public class Validator
{
public Validator(int validatorNumber)
{
ValidatorNumber = validatorNumber;
}
public int ValidatorNumber { get; }
public void Verify(IEnumerable<ForValidation> forValidations)
{
ForValidation[] copy = forValidations.ToArray();
IEnumerable<string> checkProperties = from member in copy
select member.CheckProperty;
foreach (ForValidation forValidation in copy)
{
//if (existingMembers.FirstOrDefault(m => m.CheckProperty == forValidation.CheckProperty) != null)
{
forValidation.ConfirmExistence(ValidatorNumber);
}
}
int x = copy.Length;
//each forValidation.ExistsPlaces has items until this code block
}
}

Categories