Access list from another class c# - c#

I have created two classes for my program using c#,The first one "PayRecordSettings" reads a CSV file (using CSV helper) that has 5 columns. The second class "createPayRecord" should take the list and create a sublist for each column. I managed to read the CSV file and call it from the Main and it displays the data but I haven't figured out how to take the data and pass it to other classes. This is my code
public class CsvImporter
{
public static List<PayRecordSettings> ImportPayRecords()
{
using (var path = new StreamReader("C:\\Users\\Import\\data.csv"))
{
using (var csvRead = new CsvReader(path, CultureInfo.InvariantCulture))
{
csvRead.Context.RegisterClassMap<CsvSettingsMap>();
var PayRecord = csvRead.GetRecords<PayRecordSettings>().ToList();
return PayRecord;
}
}
}
public static List<PayRecordSettings> createPayRecord()
{
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours + " hours" + details.Rate);
}
return ImportPayRecords;
}
}
public class CsvSettingsMap : ClassMap<PayRecordSettings>
{
public CsvSettingsMap()
{
Map(m => m.EmployeeId).Name("EmployeeId");
Map(m => m.Hours).Name("Hours");
Map(m => m.Rate).Name("Rate");
Map(m => m.Visa).Name("Visa");
Map(m => m.YearToDate).Name("YearToDate");
}
}
public class PayRecordSettings
{
public int EmployeeId { get; set; }
public double Hours { get; set; }
public double Rate { get; set; }
public string Visa { get; set; }
public string YearToDate { get; set; }
}
Is there a way to make empty cells equal to 0?
I have updated my code based on Fildor comments. I have to report that the it works most of it. The CSV Helper settings didnt add a 0 when there is a empty cell. I have another question, from the method PayRecordsettings once I called the list how can I store this in new sublists? if I have 5 variables I would like to store a complete column of the CSV in a new list.
public static List<PayRecordSettings> createPayRecord()
{
var payRecords = ImportPayRecords();
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours);
}
return payRecords;
} /* I would like to create a new list of Employee
list of Hours

The code that you've posted looks like it is missing some stuff. You use variables that not have been created like ImportPayRecords and payRecords in your createPayRecord-method.
To accomplish what you want to do, you need to pass your data as a parameter to you function like so:
public static void CreatePayRecord(IList<PayRecordSettings> payRecords)
{
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours + " hours" + details.Rate);
}
}
Another tip, stick to the recommended naming-conventions of C# and also try to find more descriptive names for your variables.

Is there a way to make empty cells equal to 0?
If I read the docs correctly, it should work something like this:
public class PayRecordSettings
{
public int EmployeeId { get; set; }
[Optional]
public double Hours { get; set; } = 0.0;
[Optional]
public double Rate { get; set; } = 0.0;
public string Visa { get; set; }
public string YearToDate { get; set; }
}
public class CsvSettingsMap : ClassMap<PayRecordSettings>
{
public CsvSettingsMap()
{
AutoMap(CultureInfo.InvariantCulture);
}
}
Disclaimer: Haven't tested this! So, take with a grain of salt.
"...but I havent figure out how to take the data and pass it to another classes"
You are mixing up some terms here:
public class CsvImporter // <- THIS is a class
{
// This is a (static) method
public static List<PayRecordSettings> ImportPayRecords()
{
using (var path = new StreamReader("C:\\Users\\Import\\data.csv"))
{
using (var csvRead = new CsvReader(path, CultureInfo.InvariantCulture))
{
csvRead.Context.RegisterClassMap<CsvSettingsMap>();
var PayRecord = csvRead.GetRecords<PayRecordSettings>().ToList();
return PayRecord;
}
}
}
// This is another static method.
public static List<PayRecordSettings> createPayRecord()
{
// this makes no sense: `payRecords` does not exist here.
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours + " hours" + details.Rate);
}
// makes no sense, neither. If at all, it should be `ImportPayRecords()`.
return ImportPayRecords;
}
}
So, to fix your second method, try this:
public static List<PayRecordSettings> CreatePayRecord()
{
var payRecords = ImportPayRecords();
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours + " hours" + details.Rate);
}
return payRecords;
}
Mind that there is a dependency here between the two Methods, which is ... well ... let's say "suboptimal". Also CreatePayRecord doesn't actually do much creation. So you may want to refactor to using ImportPayRecords to get the list of model instances and maybe something like this to print them out:
public static void PrintPayRecords(IEnumerable<PayRecordSettings> payRecords)
{
foreach (PayRecordSettings details in payRecords)
{
// Sticking to your format, but looks kinda weird.
Console.WriteLine("{0} hours{1}", details.Hours, details.Rate);
}
}

First issue: Getting defaulting empty cells to 0.
You can use .Default() for a default value if the field is empty.
Also, for your map class you don't need .Name("EmployeeId") if the name of the property is already "EmployeeId". It will automatically assume that is the name. If you received a CSV file that had a heading of "EmpId" then you would need Map(m => m.EmployeeId).Name("EmpId")
public class CsvSettingsMap : ClassMap<PayRecordSettings>
{
public CsvSettingsMap()
{
Map(m => m.EmployeeId);
Map(m => m.Hours).Default(0);
Map(m => m.Rate).Default(0);
Map(m => m.Visa);
Map(m => m.YearToDate);
}
}
Second issue: From the method PayRecordsettings once I called the list how can I store this in new sublists? if I have 5 variables I would like to store a complete column of the CSV in a new list.
I'm really having difficulty understanding what you are looking to do. Could you give some sample data that would be in the PayRecordSettings and then some examples of how you want the sublist data to look like? Were you looking to output it into a new CSV file?

Related

LiteDB return List of hierarchical data

I am new to LiteDb and maybe, I think to much in SQL way, instead of NoSQL way.
I try to receive a list of all child documents, which are defined within a project class.
Therefore, I have two classes generated:
public class Project : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public long Id { get; set; }
public string ProjectName { get; set; }
public List<Title> Titles { get; set; } = new List<Title>();
}
And:
public class Title : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public long Id { get; set; }
public string Title { get; set; }
}
What I try to achieve is, to get all Titles, which are stored in a specific project document.
I try the following code in C#
public async Task<IList<NamespaceTitle>> GetAllNamespacesAsync(string projectTitle)
{
var locReturnValue = await Task.Run(
() =>
{
using (var locDataBase = GetLiteDatabase())
{
var locCollection = locDataBase.GetCollection<Project>("projects");
var locGetAllTitles = locCollection.
Query().
Include(locTitle => locTitle.Titles).
Where(locProject => locProject.ProjectName == projectTitle).
Select<Title>(locTitles => locTitles.Titles).ToList();
return locGetAllTitles;
}
});
return locReturnValue;
}
The compiler error says, that a list can not be converted to Title
But when I change the
"Select(locTitles => locTitles.Titles)" in the query to
"Select<List>(locTitles => locTitles.Titles)",
the compiler error disapears, but then the return type is: "list<list>" !?
Can someone please help me, doing this in a correct way.
THX a lot in advance
I think, I found a solution for my problem.
After querying the data, I found out, that only the Id's of the child elements get saved in the master document.
So, when I do the following. Not sure, if it the common way, but it works.
var locProjectCollection = locDataBase.GetCollection<Project>("Project");
var locGetProjects = locProjectCollection.
Query().
Include(locTitles => locTitles.Titles).
Where(locProject => locProject.ProjectName == projectTitle).
ToList();
var locGetTitles = locGetProjects.
SingleOrDefault()?.
Titles.Select(locTitle => locTitle.Id);
if (locGetTitles.Count() > 0)
{
var locTitleCollection = locDataBase.GetCollection<Title>("Title");
return locTitleCollection.
Query().
Where(locTitle => locGetTitles.Contains(locTitle.Id)).
ToList();
}
else
{
return null;
}
Hope this will help other users as well - or please post an other aproach.

MongoDB Driver C#, Search by nested property using filter definition

I need help with building filter for MongoCollection of class A when I have filters for class B
public class A
{
public string ExampleAProperty { get; set; }
public B NestedB { get; set; }
public ICollection<B> NestedBCollection { get; set; }
}
public class B
{
public string ExampleBProperty { get; set; }
}
public class SearchClass
{
public async Task<ICollection<A>> SearchAsync(IMongoCollection<A> collection)
{
// this is just simple example of possible dozens predefined filters for B class.
// see FilterProvider logic used now
// var bFilter = Builders<B>.Filter.Eq(x => x.ExampleBProperty, "Example");
// in need filter A where B meets provided filters
var cursor = await collection.FindAsync(
Builders<A>.Filter.And(
// predefined filters are easy to reuse with array of elements
Builders<A>.Filter.ElemMatch(x => x.NestedBCollection, FilterProvider.SearchValue("Oleh")),
// but i did not found how to do this with single nested element
Builders<A>.Filter.Eq(x => x.NestedB, FilterProvider.SearchValue("Oleh")) // how?
)
);
return await cursor.ToListAsync();
}
}
// statics is not good but just for working example :)
public static class FilterProvider
{
public static FilterDefinition<B> SearchValue(string? value)
{
var builder = Builders<B>.Filter;
// if value is null show all
if (string.IsNullOrEmpty(value))
{
return builder.Empty;
}
// if value is "Oleh" search for "Amir"
if (value == "Oleh")
{
value = "Amir";
}
// any other additional logic to compose proper filter
// this could be search by serveral properties and so on
// however just for example i will search by hashed value :)
value = value.GetHashCode().ToString();
return builder.Eq(x => x.ExampleBProperty, value);
}
}
Please DON'T
use IMongoQueryable
propose to filter nested element by duplicate code (like x => x.NestedB.ExampleBProperty = "something")
UPDATED: example for Amir with explanation why p.2 is not than case and why code will be is duplicated if you his approach :)
As you may see we have complex (but very simple in current example) filter of data in B class. If you will use your approach - logic of composing filter for B (specified in FilterProvider.SearchValue) will be duplicated.
Thank you
You can easily do this:
public async Task<ICollection<A>> SearchAsync(IMongoCollection<A> collection, string search)
{
var bFilter = Builders<B>.Filter.Eq(x => x.ExampleBProperty, search);
var cursor = await collection.FindAsync(
Builders<A>.Filter.And(
Builders<A>.Filter.ElemMatch(x => x.NestedBCollection, bFilter),
Builders<A>.Filter.Eq(x => x.NestedB.ExampleBProperty, search)
)
);
return await cursor.ToListAsync();
}

Create Intersect of HashSet and remove Items from class structure with extracted ID's

lets assume I have the following classes:
public class ServiceStatistics
{
public string LocalId { get; set; }
public string OrganizationId { get; set; }
public List<StatisticElements> Elements { get; } = new List<StatisticElements>();
}
public class StatisticElements
{
public string StatisticId { get; set; }
public string Type { get; set; }
public string ServiceName { get; set; }
}
I retrieve such ServiceStatistics by a soap service and I use serialization/deserialization.
Each ServiceStatistics contains a set of StatisticElements. I also have a static list of StatisticElements-ID's which are relevant for calculation. All other incoming StatisticElements-ID's can be dropped. I need to do this on my side
because the SOAP Service does not support selecting specific StatisticElements-ID's
So I have generated a static Class with a HashSet:
public static class RelevantDutyPlans
{
private static HashSet<int> relevantDutyPlans;
static RelevantDutyPlans()
{
// only a subset of the original ID's
relevantDutyPlans = new HashSet<int>()
{
530,
1150,
1095,
};
}
public static HashSet<int> GetRelevantDutyPlans()
{
return relevantDutyPlans;
}
public static bool Contains(int planId)
{
return relevantDutyPlans.Contains(planId);
}
// Extracts all DutyPlans which are relevant (HashSet) for validation from
// the incoming data
public static List<int> ExtractRelevantDutyPlans(List<int> planIds)
{
var relevantPlans = new HashSet<int>(planIds);
relevantPlans.IntersectWith(relevantDutyPlans);
return relevantDutyPlans.ToList();
}
}
So my thought was, to create an Intersect like this:
List<ServiceStatistics> statistics = SoapService.GetStatistics(Now);
List<int> incomingIds = new List<int>();
foreach(var item in statistics)
{
foreach(var element in item.Statistic)
{
incomingIds.Add(int.Parse(element.StatisticId));
}
}
List<int> extract = RelevantDutyPlans.ExtractRelevantDutyPlans(incomingIds);
So now I have a List of ID's which are relevant for further processing. What I want to achieve is to remove all class elements "StatisticElements" with "StatisticId" not contained in the the extract list generated above.
Any ideas?
Any help is very appreciated
How about a little bit different approach. Simply remove irrelevant plans right away!
List<ServiceStatistics> statistics = SoapService.GetStatistics(Now);
foreach(var item in statistics)
{
item.Elements.RemoveAll(x => !RelevantDutyPlans.Contains(int.Parse(x.StatisticId)));
}
Now you are only left with the relevant once.
Hope you can use selectMany to flatten the collection and proceed the filter.
var filteredItems = statistics.SelectMany(s => s.Elements)
.Where(s => extract.Contains(Convert.ToInt32(s.StatisticId)))
.ToList();
You could also use LINQ to create a new List<> if you need to keep the original statistcs intact - e.g. if you might run multiple plans against it.
var relevantStatistics = statistics.Select(s => new {
LocalId = s.LocalId,
OrganizationId = s.OrganizationId,
Elements = s.Elements.Where(e => !RelevantDutyPlans.Contains(Convert.ToInt32(e.StatisticId))).ToList()
});
Since ServiceStatistics doesn't provide for construction, I return an anonymous object instead, but you could create an appropriate DTO class.

Accessing list of object inside an object

I have class named ResponseModel and one object inside that class named Errors refer to the class ErrorsResponseModel, and that class has bunch of objects which have List<string> data type . I would like to know on how to accessing the List<string> objects without going through like: VariableClassA.ObjectOfClassAWhichReferToTheClassB.FirstListOfString and VariableClassA.ObjectOfClassAWhichReferToTheClassB.SecondListOfString, the data of List<string> objects comes from the JSON data.
I have tried only to access one object per one object as I am not really sure on how to do generic without going through one object per one object, which is if I update the model of class B itself, then I need to make sure that I didn't missed out the necessary checking of that newly created object inside class B.
Here is the code of the model:
public sealed class ResponseModel
{
public ErrorsResponseModel Errors { get; set; }
}
public sealed class ErrorsResponseModel
{
public List<string> Username { get; set; }
public List<string> Password { get; set; }
public List<string> Nickname { get; set; }
}
Here is what I have tried so far:
string jsonData = "{"Errors":{"Username":["The username field is required."],"Password":["The password field is required."],"Nickname":["The nickname field is required."]}}";
var jsonConvertedData = JsonConvert.DeserializeObject<ResponseModel>(jsonData);
var usernameErrors = jsonConvertedData.Errors.Username;
var passwordErrors = jsonConvertedData.Errors.Password;
var nicknameErrors = jsonConvertedData.Errors.Nickname;
I expect to loop any object of class ErrorsResponseModel that the length of List<string> inside that class is more than 0 . I can't change the response data from the JSON, as it is comes from the third party.
EDIT: I have tried the following in JavaScript and it works, how can I do the same in C#?
in C#, I return to the front end like using the following return Json(jsonConvertedData), and in frontend, I do like the following:
$.ajax({
..... the AJAX settings
success: function (data) {
$.each(data.Errors, function (i, v) {
if (v.length > 0) {
console.log(v);
}
});
}
The above code in Javascript is looping through the message inside each object inside ErrorsResponseModel and read it through to the console.
Let ErrorsResponseModel inherit Dictionary
public sealed class ErrorsResponseModel : Dictionary<string, List<string>>
{
//If you still want to access data with property.
public List<string> Username => this["Username"];
...
}
Now you can loop through Errors like a normal dictionary
foreach (var item in jsonConvertedData.Errors)
if(item.Value.Count > 0)
Console.WriteLine($"{item.Key} => {item.Value[0]}");
dynamic is another choice
var jsonConvertedData = JsonConvert.DeserializeObject<dynamic>(jsonData);
foreach (var item in jsonConvertedData.Errors)
if(item.Count > 0)
foreach(var v in item.Value)
Console.WriteLine(v);
One way to do this would be to create a class to hold the related data, for example:
class User
{
public string Name { get; set; }
public string Nickname { get; set; }
public string Password { get; set; }
}
Then we can make a method that populates a list of this class from the ErrorsResponseModel class (after first validating that the counts of all the lists are the same):
public List<User> GetUsers(ErrorsResponseModel errors)
{
if (errors == null || errors.Username == null) return null;
if (errors.Username.Count == 0) return new List<User>();
if (errors.Nickname?.Count != errors.Password?.Count ||
errors.Password?.Count != errors.Username.Count)
{
throw new InvalidDataException("Unequal number of Usernames/Passwords/Nicknames");
}
return errors.Username
.Select((userName, index) =>
new User
{
Name = userName,
Nickname = errors.Nickname[index],
Password = errors.Password[index]
}).ToList();
}

C# Lists - do I use Class Methods (Get/ Set etc) again once the data is in a list?

A quick question on OOP. I am using a list together with a class and class constructor. So I use the class constructor to define the data set and then add each record to my list as the user creates them.
My questions is once the data is in the list and say I want to alter something is it good practice to find the record, create an instance using that record and then use my class methods to do whatever needs doing - and then put it back in the list?
For example below I have my class with constructor. Lets say I only want the system to release strCode if the Privacy field is set to public. Now just using Instances I would use for example Console.WriteLine(whateverproduct.ProductCode) but if the record is already in a list do i take it out of the list - create an instance and then use this method?
class Product
{
private String strCode;
private Double dblCost;
private Double dblNet;
private String strPrivacy;
public Product(String _strCode, Double _dblCost, Double _dblNet, String _strPrivacy)
{
strCode = _strCode;
dblCost = _dblCost;
dblNet = _dblNet;
strPrivacy = _strPrivacy;
}
public string ProductCode
{
get
{
if (strPrivacy == "Public")
{
return strCode;
}
else
{
return "Product Private Can't release code";
}
}
}
Lets say we have the following:
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
private string _test = "Some constant value at this point";
public string GetTest()
{
return _test;
}
public void SetTest()
{
//Nothing happens, you aren't allow to alter it.
//_test = "some constant 2";
}
}
public class Program
{
public static void Main(string[] args)
{
List<Test> listOfTest = new List<Test>()
{
new Test() {Id = 0, Name = "NumberOne", Amount = 1.0M},
new Test() {Id = 1, Name = "NumberTwo", Amount = 2.0M}
};
Test target = listOfTest.First(x => x.Id == 0);
Console.WriteLine(target.Name);
target.Name = "NumberOneUpdated";
Console.WriteLine(listOfTest.First(x => x.Id == 0).Name);
Console.WriteLine(listOfTest.First(x => x.Id == 0).GetTest());//This will alsways be "Some constant value at this point";
Console.ReadLine();
}
}
Technically you could do away with the SetTest method entirely. However, I included it to demonstrate, what it would look like, if you wanted to alter _test.
You don't want to ever create a new instance of a class, you already have an instance of. you can just alter the class where it is allowed by the author of the class, where you need to. And keep that class reference for as long as you need it. Once you are done, the reference will be garbage collected, once the program finds no active reference to your object(instance).

Categories