I have following classes:-
public class SiteMapSection
{
public string sectionUrl { get; set; }
public List<SiteMapSubSection> subSection { get; set; }
}
public class SiteMapSubSection
{
public string subSectionUrl { get; set; }
public List<SiteMapArticle> article { get; set; }
}
public class SiteMapArticle
{
public string url { get; set; }
}
I'm using SiteMapSection class as a Type in the list:-
List<SiteMapSection> siteMapSection = new List<SiteMapSection>();
Now, i'm trying to add items in 'siteMapSection' list, as given below:-
foreach (var section in sections)
{
.....
siteMapSection.Add(new SiteMapSection { sectionUrl = section.Url });
.....
foreach (var subsection in subsections)
{
.....
siteMapSection.Add(new SiteMapSubSection { ??stuck_here?? });
.....
var articles = GetNextArticles(0, subSectionId, true, false);
.....
foreach(var article in articles)
{
siteMapSection.Add(new SiteMapArticle { ??stuck_here?? });
}
}
}
How do I iterate through the collection and add items in List siteMapSection.
Updated Code, this also not works i see only siteMapSection.Add(sms) item got added but other nested still empty
List<SiteMapSection> siteMapSection = new List<SiteMapSection>();
SectionArticle sa = new SectionArticle();
foreach (BE.Section section in Sections.Find(websiteId, parentSectionId))
{
int sectionId = section.Id;
var sms = new SiteMapSection();
sms.sectionUrl = Sections.VirtualPath(section) + ".aspx";
var _subsections = new List<SiteMapSubSection>();
foreach (BE.Section subsection in Sections.Find(websiteId, sectionId))
{
int subSectionId = subsection.Id;
var smss = new SiteMapSubSection();
smss.subSectionUrl = Sections.VirtualPath(subsection) + ".aspx";
var articles = sa.GetArticlesForSection(websiteId, subSectionId, 10);
var _articles = new List<SiteMapArticle>();
foreach (var article in articles)
{
var sma = new SiteMapArticle();
sma.url = article.Code + ".aspx";
_articles.Add(sma);
}
_subsections.Add(smss);
}
siteMapSection.Add(sms);
}
I just realized that you are trying to add different types to List<SiteMapSection>. You can not add different types to a generic list. When creating a list you are defining the types which are allowed in the list, where as you are trying to add different types.
You need to change
siteMapSection.Add(new SiteMapSubSection { ??stuck_here?? });
to
siteMapSection.Add(new SiteMapSection { ??stuck_here?? });
If you provide a bit more context perhaps we could give you a better approach in general.
Hope this helps.
foreach (var section in sections)
{
.....
var sms = new SiteMapSection { sectionUrl = section.Url };
sms.subSection = new List<SiteMapSubSection>();
.....
foreach (var subsection in subsections)
{
.....
var smss = new new SiteMapSubSection { subsection }
ssms.article = new List<SiteMapArticle>();
.....
var articles = GetNextArticles(0, subSectionId, true, false);
.....
foreach(var article in articles)
{
smss.article.Add(new SiteMapArticle { article });
}
sms.subSection.Add(ssms);
}
siteMapSection.Add(sms);
}
Related
I am new to C# and OOP, in general, I've kinda hit a wall I am reading in this CSV using the CSV Helper package, but there are some unwanted rows, etc so I have cleaned it up by iterating over "records" and creating a new class LineItems.
But Now I appear to be a bit stuck. I know void doesn't return anything and is a bit of a placeholder. But How can I access all the instances of LineItems outside of this function?
public void getMapper()
{
using (var StreamReader = new StreamReader(#"D:\Data\Projects\dictUnitMapper.csv"))
{
using (var CsvReader = new CsvReader(StreamReader, CultureInfo.InvariantCulture))
{
var records = CsvReader.GetRecords<varMapper>().ToList();
foreach (var item in records)
{
if (item.name != "#N/A" && item.priority != 0)
{
LineItems lineItem = new LineItems();
lineItem.variableName = item.Items;
lineItem.variableUnit = item.Unit;
lineItem.variableGrowthCheck = item.growth;
lineItem.variableAVGCheck = item.avg;
lineItem.variableSVCheck = item.svData;
lineItem.longName = item.name;
lineItem.priority = item.priority;
}
}
}
}
}
public class LineItems
{
public string variableName;
public string variableUnit;
public bool variableGrowthCheck;
public bool variableAVGCheck;
public bool variableSVCheck;
public string longName;
public int priority;
}
public class varMapper
{
public string Items { get; set; }
public string Unit { get; set; }
public bool growth { get; set; }
public bool avg { get; set; }
public bool svData { get; set; }
public string name { get; set; }
public int priority { get; set; }
}
You should write your method to return a list.
public List<LineItems> GetMapper()
{
using (var StreamReader = new StreamReader(#"D:\Data\Projects\dictUnitMapper.csv"))
{
using (var CsvReader = new CsvHelper.CsvReader(StreamReader, CultureInfo.InvariantCulture))
{
return
CsvReader
.GetRecords<varMapper>()
.Where(item => item.name != "#N/A")
.Where(item => item.priority != 0)
.Select(item => new LineItems()
{
variableName = item.Items,
variableUnit = item.Unit,
variableGrowthCheck = item.growth,
variableAVGCheck = item.avg,
variableSVCheck = item.svData,
longName = item.name,
priority = item.priority,
})
.ToList();
}
}
}
Here's an alternative syntax for building the return value:
return
(
from item in CsvReader.GetRecords<varMapper>()
where item.name != "#N/A"
where item.priority != 0
select new LineItems()
{
variableName = item.Items,
variableUnit = item.Unit,
variableGrowthCheck = item.growth,
variableAVGCheck = item.avg,
variableSVCheck = item.svData,
longName = item.name,
priority = item.priority,
}
).ToList();
I have a List of IJapaneseDictionaryEntry objects which are described below. Inside this are IKanji objects that contain Priorites objects.
I have a rather difficult thing I would like to do and would appreciate any advice / suggestions. What I would like to do is to retrieve entries that have an entry that have Priority of "Frequency1" have Priority of "Frequency2" or Priority of "Frequency3" from the list entries that I created.
public interface IJapaneseDictionaryEntry
{
int Sequence { get; }
IEnumerable<IKanji> Kanjis { get; }
IEnumerable<IReading> Readings { get; }
IEnumerable<ISense> Senses { get; }
}
Where each object contains a list of IKanji objects
public interface IKanji
{
string Text { get; }
IEnumerable<KanjiInformation> Informations { get; }
IEnumerable<Priority> Priorities { get; }
}
Here's the list:
List<IJapaneseDictionaryEntry> entries = dictionary.GetEntries().ToList();
Here's a view that I think might help explain the contents:
I hope the information here is enough as it seems difficult to explain what I need to retrieve.
var result = entries.Where(e => e.Kanjis.Any(k => k.Priorities.Contains(Priority.Frequency1) ||
k.Priorities.Contains(Priority.Frequency2) ||
k.Priorities.Contains(Priority.Frequency3)
)).ToList();
Considering your 2 questions, I would have made something like this:
[Flags]
public enum Priority
{
Frequency1 = 1,
Frequency2 = 2,
Frequency3 = 4,
Frequency4 = 8
}
public interface IKanji
{
string Text { get; }
IEnumerable<KanjiInformation> Informations { get; }
Priority Priorities { get; }
}
In above consider each Priority as a bit in an int, you can add priority by using bitwise or (|) :
Priorities = Priority.Frequency1 | Priority.Frequency2 // means have both priorities
To check if it has specific priority use bitwise and (&):
if((Priorities & Priority.Frequency1) == Priority.Frequency1
{
// it contains Priority.Frequency1
}
Then the answer you were looking for will be like:
Priority p = Priority.Frequency1 | Priority.Frequency2 | Priority.Frequency3
var result = entries.Where(e => e.Kanjis.Any(k => k.Priorities & p == p)))
.ToList();
This could be one solution:
var filteredEntries = entries.Where( // Only entries
e => e.Kanjis.Any( // which have one or more kanjis with..
a => a.Priorities.Any( // which have one or more priorities
p => p.Value == "Frequency1" // which have a value of "Frequency1"
)));
I changed your interfaces to classes to make it run with some example-data:
public class IJapaneseDictionaryEntry
{
public int Sequence { get; set; }
public IEnumerable<IKanji> Kanjis { get; set; }
}
public class IKanji
{
public string Text { get; set; }
public IEnumerable<Priority> Priorities { get; set; }
}
public class Priority
{
public string Value { get; set; }
}
public static void Main(string[] args)
{
// Initialize 3 objects. One has Priority we're searching
List<IJapaneseDictionaryEntry> entries = new List<IJapaneseDictionaryEntry>()
{
new IJapaneseDictionaryEntry(){ Sequence = 1, Kanjis = new List<IKanji>() { new IKanji() { Priorities = new List<Priority>() { new Priority() { Value = "Frequency1" } } } } },
new IJapaneseDictionaryEntry(){ Sequence = 2, Kanjis = new List<IKanji>() { new IKanji() { Priorities = new List<Priority>() { new Priority() { Value = "Frequency2" } } } } },
new IJapaneseDictionaryEntry(){ Sequence = 3, Kanjis = new List<IKanji>() { new IKanji() { Priorities = new List<Priority>() { new Priority() { } } } } },
};
// Here's the magic:
var filteredEntries = entries.Where( // Only entries
e => e.Kanjis.Any( // which have one or more kanjis with..
a => a.Priorities.Any( // which have one or more priorities
p => p.Value == "Frequency1" // which have a value of "Frequency1"
)));
// Let's check the output
foreach (var e in filteredEntries)
{
Console.WriteLine(e.Sequence);
}
}
I am writing a class that reads different kinds of CSV files. It picks out the important information based on Model classes, where the properties of the model class are the column names that I want to grab. For example, I could have an OutlookModel with columns FromAddress and ToAddress. Or I could have a SalesforceModel with totally different columns.
When the reader class parses through the rows and columns, it loads up the cells into an instance of the model class. In the code below, the argument className = OutlookModel. The most relevant lines of code here are the signature and the return...
protected void MapColumns(string row, string className, List<OutlookModel> list)
{
string[] cols = row.Split(',');
// create a model to save the important columns
var model = Activator.CreateInstance(nameSpace, nameSpace + className);
int j = 0;
if (cols.Length > 0)
{
foreach (var c in cols)
{
// is this column index one of our important columns?
if (Ordinals.ContainsKey(j))
{
// this is a column we care about, so set the model property
model.GetType().GetProperty(Ordinals[j]).SetValue(model, c);
}
j++;
}
}
list.Add(model);
}
The problem I am having is the collection of model objects. If I define the object as List< OutlookModel > in the arguments, then the method is not extensible. If I define it as List< object >, then (i think) I have to cast the inside list to use my properties which are all different between the models.
I am fairly new to C#. Is there a better way to capture these different model types into a list/array/collection/whatever so that I can then apply logic to the lists?
So first of all i suggest to add a custom attribute to mark the properties you want to read from the csv, so you don't run into any problem when you have to add something later and you don't have to rely on too many magic strings. Here is my test setup:
class ReadFromCsvAttribute : Attribute { }
class OutlookModel
{
public int DontSetThisValueFromCsv { get; set; }
[ReadFromCsv]
public string FromAddress { get; set; }
[ReadFromCsv]
public string ToAddress { get; set; }
}
class SalesForceModel
{
[ReadFromCsv]
public string Name { get; set; }
[ReadFromCsv]
public string Age { get; set; }
}
static void Main(string[] args)
{
string outlookSample = "Id,FromAddress,ToAddress,Useless\r\n" +
"1,a#b.com,c#d.com,asdf\r\n" +
"3,y#z.com,foo#bar.com,baz";
string salesForceSample = "Id,Name,Age\r\n" +
"1,John,30\r\n" +
"2,Doe,100";
var outlook = ReadFromCsv<OutlookModel>(outlookSample);
var salesForce = ReadFromCsv<SalesForceModel>(salesForceSample);
}
I put together this generic method to read whatever model you want from the data:
static List<T> ReadFromCsv<T>(string data)
{
var objs = new List<T>();
var rows = data.Split(new[] {"\r\n"}, StringSplitOptions.None);
//create index, header dict
var headers = rows[0].Split(',').Select((value, index) => new {value, index})
.ToDictionary(pair => pair.index, pair => pair.value);
//get properties to find and cache them for the moment
var propertiesToFind = typeof (T).GetProperties().Where(x => x.GetCustomAttributes<ReadFromCsvAttribute>().Any());
//create index, propertyinfo dict
var indexToPropertyDict =
headers.Where(kv => propertiesToFind.Select(x => x.Name).Contains(kv.Value))
.ToDictionary(x => x.Key, x => propertiesToFind.Single(p => p.Name == x.Value));
foreach (var row in rows.Skip(1))
{
var obj = (T)Activator.CreateInstance(typeof(T));
var cells = row.Split(',');
for (int i = 0; i < cells.Length; i++)
{
if (indexToPropertyDict.ContainsKey(i))
{
//set data
indexToPropertyDict[i].SetValue(obj, cells[i]);
}
}
objs.Add(obj);
}
return objs;
}
Here's another sample. Since you're new to c#, I've avoided linq and extension methods as much as possible. Just copy it into a console app and run.
Also, I like theHennyy recommendation of using .net attributes to describe a class but only if you have full control of your ecosystem.
public class Account
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class LastNameAccount
{
public string LastName { get; set; }
public string Address { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
Test1();
}
private static void Test1()
{
/*
* defines the result of your CSV parsing.
*/
List<string> csvColumns = new List<string> { "FirstName", "LastName" };
List<List<string>> csvRows = new List<List<string>>() {
new List<string>(){"John","Doe"},
new List<string>(){"Bill", "Nie"}
};
//Map the CSV files to Account type and output it
var accounts = Map<Account>(csvColumns, csvRows);
if (accounts != null)
{
foreach (var a in accounts)
{
Console.WriteLine("Account: {0} {1}", a.FirstName, a.LastName);
}
}
//Map the CSV files to LastNameAccount type and output it
var accounts2 = Map<LastNameAccount>(csvColumns, csvRows);
if (accounts2 != null)
{
foreach (var a in accounts2)
{
Console.WriteLine("Last Name Account: {0} {1}", a.LastName, a.Address);
}
}
}
private static List<T> Map<T>(List<string> columns, List<List<string>> rows)
where T : class, new()
{
//reflect the type once and get valid columns
Type typeT = typeof(T);
Dictionary<int, PropertyInfo> validColumns = new Dictionary<int, PropertyInfo>();
for (int columnIndex = 0; columnIndex < columns.Count; columnIndex++)
{
var propertyInfo = typeT.GetProperty(columns[columnIndex]);
if (propertyInfo != null)
{
validColumns.Add(columnIndex, propertyInfo);
}
}
//start mapping to T
List<T> output = null;
if (validColumns.Count > 0)
{
output = new List<T>();
foreach (var row in rows)
{
//create new T
var tempT = new T();
//populate T's properties
foreach (var col in validColumns)
{
var propertyInfo = col.Value;
var columnIndex = col.Key;
propertyInfo.SetValue(tempT, row[columnIndex]);
}
//add it
output.Add(tempT);
}
}
return output;
}
}
public class kingdomAddModel
{
public string title { get; set; }
public string details { get; set; }
//public HttpPostedFileBase fileUpload { get; set; }
//public string retrieveFile { get; set; }
public FileAttr files { get; set; }
}
public class FileAttr
{
public HttpPostedFileBase fileUpload { get; set; }
public string retrieveFile { get; set; }
}
var getDailyDevotions = db.DailyDevotions.Select(d => new { title = d.DevotionsTitle, details = d.DevotionsDetails, retriveFileAudio = d.VoiceNotes });
List<kingdomAddModel> listdevotions = new List<kingdomAddModel>();
foreach (var getDevotions in getDailyDevotions)
{
kingdomlist = new kingdomAddModel();
kingdomlist.title = getDevotions.title;
kingdomlist.details = getDevotions.details;
fileattr = new FileAttr();
fileattr.retrieveFile = getDevotions.retriveFileAudio;
kingdomlist.files.retrieveFile = fileattr.retrieveFile; //erros appears here!
}
The line line kingdomlist.files.retrieveFile throws the exception, tried googling but I dont get simular problem. I just want to assign the value and will pull on my view.
Do not access properties of FileAttr directly, only use files with the instance of kingdomAddModel. Don't mixup them
Replace
foreach (var getDevotions in getDailyDevotions)
{
kingdomlist = new kingdomAddModel();
kingdomlist.title = getDevotions.title;
kingdomlist.details = getDevotions.details;
fileattr = new FileAttr();
fileattr.retrieveFile = getDevotions.retriveFileAudio;
kingdomlist.files.retrieveFile = fileattr.retrieveFile; //erros appears here!
}
with
foreach (var getDevotions in getDailyDevotions)
{
kingdomlist = new kingdomAddModel
{
title = getDevotions.title,
details = getDevotions.details,
files = new FileAttr
{
retrieveFile = getDevotions.retriveFileAudio,
//fileUpload = some value here
}
};
listdevotions.Add(kingdomlist);
}
OR use Linq
listdevotions = (from getDevotions in getDailyDevotions
select new kingdomAddModel
{
title = getDevotions.title,
details = getDevotions.details,
files = new FileAttr
{
retrieveFile = getDevotions.retriveFileAudio,
//fileUpload = some value here
}
}).ToList();
The issue here is that the instance of the class "obj" is re-created every time I run through the loop so at the end of the loop, I only have 1 set of the object. It should have several.
foreach (var project in projectsDictionary)
{
foreach (var season in seasonsDictionary)
{
foreach (var episode in episodesDictionary)
{
obj = new Parent
{
Title = project.Value, Link = "1", Children = new List<Parent>
{
new Parent
{
Title = season.Value, Link = "1", Children = new List<Parent>
{
new Parent
{
Title = episode.Value, Link = "1", Children = null
}
}
}
}
};
}
}
}
var responseBody = JsonConvert.SerializeObject(obj);
return responseBody;
public class Parent
{
public string Title
{
get;
set;
}
public string Link
{
get;
set;
}
public List<Parent> Children
{
get;
set;
}
}
Outside the first loop define obj as a list.
var obj = new List<Parent>();
then
obj.Add(new Parent(...));