Create a object hierarchy from a list of folder locations - c#

I have a list of locations as strings;
locA/locB
locA/locB/locH
locC/locD/locE
locC/locD/locE/locK
locF/locG
I've been trying to create an object that uses the same structure as the list of locations passed to it;
e.g. Something like..
var myHObject=CreateHeirarchicalObjectFromList(myStringListOfLocations);
I'm having problems looping through the list without almost doing it manually with loads of loops. Is there an easier way, maybe recursion?
I want to end up with an object like this;
.locA
.locB
.locH
.locC
.locD
.locE
.locK
.locF
.locG
That I can use to create a visual hierarchy.

Prob not the best but knocked up in LinqPad, will reformat in a sec..
void Main()
{
var strings = new string[]{"locA/locB","locA/locB/locH",
"locC/locD/locE","locC/locD/locE/locK","locF/locG"};
var folders = Folder.Parse(strings);
folders.Dump();
}
public class Folder
{
public string Name { get; set; }
public List<Folder> Folders { get; internal set; }
public Folder()
{
Folders = new List<Folder>();
}
//Presume that each string will be folder1/folder2/folder3
public static IEnumerable<Folder> Parse(IEnumerable<string> locations)
{
var folders = new List<Folder>();
foreach (string location in locations)
{
string[] parts = location.Split('/');
Folder current = null;
foreach (string part in parts)
{
var useFolders = current != null ?
current.Folders : folders;
current = useFolders.SingleOrDefault(f => f.Name == part) ?? new Folder() { Name = part };
if (!useFolders.Contains(current)) { useFolders.Add(current); }
}
}
return folders;
}
}

Related

C# - List of Objects - Acces an object's parameter

I am currently in the middle of trying to create a simple rpg C# game and I am stuck fairly early on in the map creation section.
I created a class for my locations:
public class Location
{
private int ID;
private string Name;
public Location(int id, string name)
{
ID = id;
Name = name;
}
}
And I also created a method which fills a list with my locations. Outside in the main program area I created the list so it is accesible by everything:
List<Location> map = new List<Location>();
public void CreateMap()
{
Location start = new Location(1, "START");
Location forest = new Location(2, "FOREST");
Location city = new Location(3, "CITY");
map.Add(start);
map.Add(forest);
map.Add(city);
}
CreateMap();
However, I am stuck now, because I do not know how to access the parameters of my Location objects inside the list, as I only found how to access a string from a list on the internet, or very complicated and confusing answers that I did not at all understand. I wanted to somehow use a static class as I learned that they are usefull when we do not want to access the information inside the class, but I reckon I didn't quite grasp their concept.
tl;dr:I want to take my ID/Name of a specific object out of my list, but I do not know how.
Change your Location class to use public properties:
public class Location
{
public int Id {get;set;}
public string Name {get;set;}
}
Your CreateMap would then look like this:
List<Location> map = CreateMap();
public List<Location> CreateMap()
{
return new List<Location> {
new Location {Id=1, Name="START"},
new Location {Id=2, Name="FOREST"},
new Location {Id=3, Name="CITY"}
};
}
You can then reference Locations like this:
map.First(m=>m.Id==1).Name
Although, I would suspect you will be doing a lot of lookups by Id, and your List should more than likely be a Dictionary, or a simple array where the index is the Id instead, which will make location lookups much faster. In that case, you can easily convert to a dictionary like this:
Dictionary<int,Location> mapDict = CreateMap.ToDictionary(k=>k.Id, v=>v);
Then you can access location by id like this:
var location = mapDict[1];
var name = location.Name;
Declare Location members as public so you can use List.Find(), for example:
Location start = map.Find(l => l.Name == "START");
Some useful information about List.Find() here.
If your Location class properties are public then you can iterate the list via foreach or for loop as shown below:
foreach(var location in map)
{
var locationId = location.ID;
var locationName = location.Name;
}
OR
for(var i; i< map.Count; i++)
{
var locationId = map[i].ID;
var locationName = map[i].Name;
}
If Location class properties are private, then you will need to add a public function to Location class to do the job. But it won't be an elegant solution:
public class Location
{
private int ID;
private string Name;
public Location(int id, string name)
{
ID = id;
Name = name;
}
public Tuple<int, string> GetById(int id)
{
if (id == this.ID)
{
return new Tuple<int, string>(this.ID, this.Name);
}
else
{
return new Tuple<int, string>(-1, "Not Found");
}
}
}
and then outside you can access it like:
var map = new List<Location>();
map.Add(new Location(10, "A"));
map.Add(new Location(20, "B"));
map.Add(new Location(30, "C"));
var bFound = false;
Tuple<int, string> locationTuple;
foreach (var location in map)
{
var byId = location.GetById(20);
if (byId.Item1 > -1)
{
locationTuple = new Tuple<int, string>(byId.Item1, byId.Item2);
break;
}
}

Turn array into a POCO object

I'm having trouble converting the following string array into a POCO object.
Given the following:
string files = [
"./Folder/file.ext",
"./Folder/file2.ext",
"./Folder/file3.ext",
"./Folder/nestedfolder/file.ext",
"./Folder2/file1.ext",
"./Folder2/file2.ext",
"./file1.ext",
"./file2.ext",
"./file3.ext",
];
I would like to convert it to something like:
public class HierarchicalSource{
public List<HierarchicalSource> Children = new List <HierarchicalSource> ();
public bool folder { get; set; }
public string FullPath;
public HierarchicalSourceSource(string path) {
this.FullPath = path;
}
}
Where HierarchicalSource is the root, and has a list of children
UPDATE:
I ended up changing the list to a dictionary. There must be a more efficient way to do this, but I did as follows:
string fileList = files.Select(x => x.Remove(0, 2)).ToArray();
var root = new HierarchicalSource("root");
foreach(var f in fileList){
var current = root;
string[] splitFile = f.Split('/');
foreach(var s in splitFile){
if(!current.Children.ContainsKey(s)){
current.Children.Add(s, new List<HierarchicalSource>{ new HierarchicalSource(s) });
}
current = current.Children[s].Last();
}
}
POCO:
public class HierarchicalSource{
public string name;
public Dictionary<string, List<HierarchicalSource>> Children = new Dictionary<string, List<HierarchicalSource>>();
public HierarchicalSource(string name){
this.name = name;
}
}
If I understand you correctly, this requires looping through the array, but it'll allow you to parse each item in the array so you can generate the HierarchicalNode object's values.
var node = new HierarchicalSource();
foreach(var str in files)
{
var pathParts = str.Split('/').ToList();
node.Children.Add(new HierarchicalNode()
{
FullPath = str,
Folder = pathParts[1] // you may need to do some debugging to see what the results for pathParts are instead of just [#]
});
}
Since the FullPath member in HierarchicalNode is public you can set that value without having to go through any constructor.
// using the above code for reference
node.FullPath = whateverThePathYouNeedIs;
Also update that property in the class to use getters and setters
public string FullPath { get; set; }

Reading file during LINQ query

I have a simple class, and I want to have the results from:
(which are correct so far)
Console.WriteLine(f.temp1);
Console.WriteLine(f.temp2);
in my Class Definitions temp1=Name; temp2=id
public class Definitions
{
public string Name { get; set; }
public string Id { get; set; }
}
class Program
{
static void Main()
{
ReadDefinitions();
}
public static void ReadDefinitions()
{
var files = from name in Directory.EnumerateFiles(Settings.Folder)
from id in File.ReadLines(name).Skip(2).Take(1)
select new
{
temp1= Path.GetFileNameWithoutExtension(name),
temp2= id
};
foreach (var f in files)
{
Console.WriteLine(f.temp1);
Console.WriteLine(f.temp2);
}
foreach (var f in files)
{
Console.WriteLine(f.temp1);
Console.WriteLine(f.temp2);
}
}
}
I know this is stupid with this temp stuff, but I could not manage to do it directly. :(
The goal is to:
Read the directory with many thousand files...
Put the name into Definitions.Name
Put line 3 of every file into Definitions.Id
So that I can access them anytime in my Program.
(I still need to trim the 3 left characters of line AND the 4 right characters of it,..but I'll probably manage that myself)
If understand correctly you just need to do this
var files = from name in Directory.EnumerateFiles(Settings.Folder)
select new
{
temp1= Path.GetFileNameWithoutExtension(name),
temp2= File.ReadLines(name).Skip(2).First()
};
If you want to skip the temp stuff then you can:
var files = from name in Directory.EnumerateFiles(Settings.Folder)
select new Definitions
{
Name = Path.GetFileNameWithoutExtension(name),
Id = File.ReadLines(name).Skip(2).First()
};

Better Way to Iterate Class Properties

I have the following class called HIP
using System;
namespace Shared
{
public class HIP
{
public HIP ()
{
}
public double data_source { get; set; }
public string hid { get; set; }
public double wid { get; set; }
public double psn { get; set; }
}
}
And I got the oData and adding each properties to the List as follows:
var client= new ODataClient(settings);
var packages =await client.For("HIP").FindEntriesAsync();
protected List<HIP> hcp = new List<HIP>();
foreach (var package in packages)
{
hcp.Add(new HIP {wid=Convert.ToSingle(package["wid"])});
hcp.Add(new HIP {hid=package["hid"].ToString()});
hcp.Add(new HIP {psn=Convert.ToSingle(package["psn"])});
hcp.Add(new HIP {data_source=Convert.ToSingle(package["data_source"])});
}
My question is how to get foreach operation in optimal/better way. Now, I have 4-5 properties and I could type each property names as follows package["wid"],package["hid"],package["psn"],package["data_source"]; however what if I have a tens of properties. I would like to know is there a better way to iterate.
You can use reflection to do something along the lines of:
var hcp = new List<HIP>();
var hipProperties = typeof(HIP).GetProperties();
hcp.AddRange(hipProperties.Select(prop =>
{
var hip = new HIP();
prop.SetValue(hip, Convert.ChangeType(package[prop.Name], prop.PropertyType), null);
return hip;
}).ToList();
The above code will generate a list of HIP objects with only a single property set on each. I believe you might want to create a single HIP object with all its properties set for each package, like this:
var client = new ODataClient(settings);
var packages = await client.For("HIP").FindEntriesAsync();
var hcp = new List<HIP>();
var properties = typeof(Hip).GetProperties();
foreach (var p in packages)
{
var hip = new HIP();
foreach (var prop in properties)
{
prop.SetValue(hip, Convert.ChangeType(p[prop.Name], prop.PropertyType), null);
}
hcp.Add(hip);
}

Create nested objects dynamically

I am using third party library where if I wanted to created nested structure for directories I have to create like this
new ClassA("folder1", new ClassA("folder2", new ClassA("folder3")));
this will create folder structure like this folder1->folder2->folder3.
To make it simple for my users I am creating methods where users pass the path as parameter and my method process the path and should create the above object structure which internally creates folder structure.
right now I am able to parse the path like a tree but could not able to create above object structure.
This is the sample console application code
class Program
{
static void Main(string[] args)
{
List<string> Paths = new List<string>();
Paths.Add("D1");
Paths.Add("E1");
Paths.Add(#"E1\E11");
Paths.Add(#"D1\D11");
Paths.Add(#"D1\D12");
Paths.Add(#"D1\D2");
Paths.Add(#"D1\D2\D21");
Node nodeObj = new Node();
foreach (var path in Paths)
{
nodeObj.AddPath(path);
}
//var nodes = nodeObj.Nodes;
Node current = nodeObj;
int level = 0;
ReadNodes(current, level);
}
private static void ReadNodes(Node current, int level)
{
foreach (string key in current.Nodes.Keys)
{
var tablevel = level;
string tab = string.Empty;
while (tablevel>0)
{
tab = tab + " ";
tablevel--;
}
Console.WriteLine(tab +":" + key);
// The child node.
Node child;
if (current.Nodes.TryGetValue(key, out child) && child.Nodes.Count>0)
{
ReadNodes(child, level+1);
}
else { }
}
}
}
public class Node
{
private readonly IDictionary<string, Node> _nodes =
new Dictionary<string, Node>();
public IDictionary<string, Node> Nodes
{
get { return _nodes; }
}
private string Path { get; set; }
public string Source { get; set; }
public void AddPath(string path)
{
char[] charSeparators = new char[] { '\\' };
// Parse into a sequence of parts.
string[] parts = path.Split(charSeparators,
StringSplitOptions.RemoveEmptyEntries);
// The current node. Start with this.
Node current = this;
// Iterate through the parts.
foreach (string part in parts)
{
// The child node.
Node child;
// Does the part exist in the current node? If
// not, then add.
if (!current._nodes.TryGetValue(part, out child))
{
// Add the child.
child = new Node
{
Path = part
};
// Add to the dictionary.
current._nodes[part] = child;
}
// Set the current to the child.
current = child;
}
}
}
class Test : yourClass
{
string name;
Test childTest;
public Test(string name)
{
this.name = name;
this.childTest = null;
}
public Test(string name, Test test)
{
this.name = name;
this.childTest = test;
}
}
Test a = new Test("a", new Test("b", new Test("c")));
Are you just searching for a structure like this? You need two constructors one that accepts only a string and one that accepts a string and another class of the same type

Categories

Resources