Create a list of objects with initialized properties from a string with infos

Create a list of objects with initialized properties from a string with infos



I have a string that looks like that:


random text 12234
another random text

User infos:

User name : John
ID : 221223
Date : 23.02.2018
Job: job1

User name : Andrew
ID : 378292
Date : 12.08.2017
Job: job2

User name : Chris
ID : 930712
Date : 05.11.2016
Job : job3

some random text



And this class:


class User

public string UserName get; set;
public string ID get; set;
public string Date get; set;
public string Job get; set;
public User(string _UserName, string _ID, string _Date, string _Job)

UserName = _UserName
ID = _ID;
Date = _Date;
Job = _Job;




And I want to create a List of Users with informations from that string.



I have tried doing that:


List<User> Users = new List<User>();

string Data = (the data above)
string lines = Data.Split(new string Environment.NewLine , StringSplitOptions.RemoveEmptyEntries);

List<string> UserNames = new List<string>();
List<string> IDs = new List<string>();
List<string> Dates = new List<string>();
List<string> Jobs = new List<string>();

foreach (var line in lines)

if (line.StartsWith("User name : "))

UserNames.Add(Line.Remove(0, 12));


if (Line.StartsWith("ID : "))

IDs.Add(Line.Remove(0, 5));


if (Line.StartsWith("Date : "))

Dates.Add(Line.Remove(0, 7));


if (Line.StartsWith("Job : "))

Jobs.Add(Line.Remove(0, 6));


var AllData = UserNames.Zip(IDs, (u, i) => new UserName = u, ID = i );

foreach (var data in AllData)

Users.Add(new User(data.UserName, data.ID, "date", "job"));



But I can only combine two lists using this code. Also, I have more than 4 values for each user (the string above was just a short example) .



Is there a better method? Thanks.





is the string is in withing json format?
– styx
Sep 4 '18 at 12:44





No, it is just plain text. You can see the example above.
– asfdev991
Sep 4 '18 at 12:45





Welcome to StackOverflow @asfdev991. Please remember to add extra information about the question into the question itself so that future readers don't have to trawl the comments to find it ;)
– Ian
Sep 4 '18 at 12:53





do you read it from a file? is there always this empty line between the entries?
– Mong Zhu
Sep 4 '18 at 13:03





Yes, there is always a empty line between the entries. And the string also has other text in it, so I should ignore it. I have updated the question.
– asfdev991
Sep 4 '18 at 13:07




4 Answers
4



Since it seems to be always 4 lines of information you could go in steps of 4 with a loop through the splitted array lines. At each step you would split by colon : and collect the last item, which is the desired value:


4


lines


:



EDIT: In this case I would suggets to look for the START of the data.


int startIndex = Data.IndexOf("User name");



EDIT 2:



also ends with another line of text



then you can use LastIndexOf to find the end of the important information:


int endIndex = Data.LastIndexOf("Job");
int lengthOfLastLine = Data.Substring(endIndex).IndexOf(Environment.NewLine);
endIndex += lengthOfLastLine;



and then simply take a SubString from the startindex on until the end


string lines = Data.Substring(startIndex, endIndex - startIndex)
.Split(new string Environment.NewLine , StringSplitOptions.RemoveEmptyEntries);
List<User> allUsers = new List<UserQuery.User>();

for (int i = 0; i < lines.Length; i += 4)

string name = lines[i].Split(':').Last().Trim();
string ID = lines[i + 1].Split(':').Last().Trim();
string Date = lines[i + 2].Split(':').Last().Trim();
string Job = lines[i + 3].Split(':').Last().Trim();
allUsers.Add(new User(name, ID, Date, Job));



Ahhh, and you should Trim the spaces away.
This solution should be readable. The hard coded step size of 4 is actually annoying in my solution


Trim


4



Disclaimer: This solution works only as long as the format does not change. If the order of the lines should change, it will return false results





Great idea. I prefer Remove to Split() though.
– aloisdg
Sep 4 '18 at 12:57


Remove


Split()





With semicolon it is the same as .csv format
– Fabjan
Sep 4 '18 at 13:00






I forgot to say that the string also ends with another line of text (you can see the updated example in the question). In reality, each entry has 7 lines (not 4 as I said above), so I have changed "i +=4" to "i+=7" and I have added the remaining string (until string ExString = lines[i + 6].Split(':').Last().Trim();) but now I get "Index was outside the bounds of the array."
– asfdev991
Sep 4 '18 at 13:30






@asfdev991 something else you forgot to say ? ;) hehe ok, there is a method: LastIndexOf actually you should have enough information to fix it on your own now ;) You can use it to calculate the real endIndex then take this overload of SubString and get only the important part
– Mong Zhu
Sep 4 '18 at 13:40






Yes, I have fixed the issue.
– asfdev991
Sep 4 '18 at 14:02



Instead of checking each line to add each of them to a a list, you can create your list of User directly. There you go:



Code:


var users = data.Split(new "nn" , StringSplitOptions.None).Select(lines =>

var line = lines.Split(new "n" , StringSplitOptions.None);
return new User(line[0].Substring(11), line[1].Substring(4), line[2].Substring(6), line[3].Substring(5));
);



Try it online!



As @Mong Zhu answer, remove everything before and after. A this point, this is another question I wont try to solve. Remove the noise before and after then parse your data.





The 3rd record (for "chris") is different from the rest
– Tian van Heerden
Sep 4 '18 at 13:26





@TianvanHeerden updated :)
– aloisdg
Sep 4 '18 at 13:38





Yes, I made a mistake.
– asfdev991
Sep 4 '18 at 13:39



For a robust, flexible and self-documenting solution that will allow you to easily add new fields, ignore all the extraneous text and also cater for variations in your file format (this seems to be the case with, for example, no space in "ID:" only in the 3rd record), I would use a Regex and some LINQ to return a collection of records as follows:


Regex


using System.Text.RegularExpressions;

public class Record

public string Name get; set;
public string ID get; set;
public string Date get; set;
public string Job get; set;

public List<Record> Test()

string s = @"User name : John
ID : 221223
Date : 23.02.2018
Job: job1

User name : Andrew
ID : 378292
Date : 12.08.2017
Job: job2

User name : Chris
ID: 930712
Date : 05.11.2016
Job: job3
";
Regex r = new Regex(@"Usersnames:s(?<name>w+).*?IDs:s(?<id>w+).*?Dates:s(?<date>[0-9.]+).*?Job:s(?<job>ww+)",RegexOptions.Singleline);
r.Matches(s);
return (from Match m in r.Matches(s)
select new Record

Name = m.Groups["name"].Value,
ID = m.Groups["id"].Value,
Date = m.Groups["date"].Value,
Job = m.Groups["job"].Value
).ToList();



The CSV format seems to be what you're looking for (since you want to add some header to this file the actual CSV stars on 6th line):


random text 12234
another random text

User infos:

UserName;ID;Date;Job
John;221223;23.02.2018;job1
Andrew;378292;12.08.2017;job2
Chris;930712;05.11.2016;job3



And then you could read this file and parse it:


var lines = File.ReadAllLines("pathToFile");
var dataStartIndex = lines.IndexOf("UserName;ID;Date;Job");
var Users = lines.Skip(dataStartIndex + 1).Select(s =>

var splittedStr = s.Split(';');
return new User(splittedStr[0], splittedStr[1], splittedStr[2], splittedStr[3]);
).ToList();



If you're working with console entry just skip the header part and let user enter comma separated values for each user on a different string. Parse it in a same way:


var splittedStr = ReadLine().Split(';');
var userToAdd = new User(splittedStr[0], splittedStr[1], splittedStr[2] , splittedStr[3]);
Users.Add(userToAdd);





new User().ToList() wont work. Also, you dont need it if users doesnt need to be a list.
– aloisdg
Sep 4 '18 at 13:40


new User().ToList()


users





The string above is not in the CSV format. It looks more like a INI file, but with a ":"
– asfdev991
Sep 4 '18 at 13:44





Yup, it's CSV-like file, sorry for typos, I've corrected them
– Fabjan
Sep 4 '18 at 13:53



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Some of your past answers have not been well-received, and you're in danger of being blocked from answering.



Please pay close attention to the following guidance:



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)