Separating a CSV file contents into an array - c#

My CSV file is formatted like this
Object,Value,Attribute
Object,Value,Attribute
Object,Value,Attribute
etc.
Would I need 3 separate arrays? (I think that's the best way of doing this..) One for object which I can then feed into my chart.. one for value and the attribute. I don't know much about arrays yet.

You should create your own class that holds Object, Value and Attribute and store this in a list.
class SomeClass {
public string MyObject { get; set; }
public string MyValue { get; set; }
public string MyAttribute { get; set; }
}
private List<SomeClass> myList = new List<SomeClass>();
public void ReadCsv(){
using (var sr = new StreamReader("PathToCsvFile")) {
string currentLine;
while ((currentLine = sr.ReadLine()) != null) {
var elements = currentLine.Split(',');
myList.add(new SomeClass {
MyObject = elements[0],
MyValue = elements[1],
MyAttribute = elements[2]
});
}
}
}

I'd recommend one array of a class that holds object, value, and attribute, because then you don't need to worry about the complications of missing data, and changing all your arrays if you add more columns.

What you do with the fields depends on how you're going to use the data. You probably need an array of structs or objects, where each element in the array is a row and each member of the object/struct is a column.
Here's a very simple example of a struct, where you can hold your data:
struct MyStruct
{
string Column1;
string Column2;
//etc
}
And here's some code to populate it from the file:
List<MyStruct> rows = new List<MyStruct>;
s = reader.ReadLine();
while (s != null)
{
string s[] columns = SplitLine(s);
MyStruct row = new MyStruct();
row.Column1 = s[0];
row.Column2 = s[1];
rows.Add(row);
s = reader.ReadLine();
}
Notice the ambiguous function "SplitLine." Lots of ways to split a string up . Look here for the best ways to split the string into fields.

It really depends on what you want to do with the data. It seems you want to chart the data per category. If that is the case then the simplest way is to put each col in the same list.
1) read the csv file, per line
2) split the line based on comma
3) put data (in the same column) to the same list

Related

Is it possible to make a variable that can be an array or a non-array?

I want to be able to do something like
public string[]|string stringsOrSingleString;
I want to create a variable that can be an array or a non-array of a specific type (a string in the example).
Example usage
I want to be able to do stringsOrSingleString = "bla" or stringsOrSingleString = new string[] { "bla" };
Do I need a custom class to do this? Preferably, I don't want to use a custom class, but if necessary then ok.
I should be able to tell later on if the value assigned was an array or non-array, using typeof or is, or something.
The whole reason for this ordeal is that I have a javascript API(that I didn't create), and I am trying to make a C# api that follows the JS api/syntax as close as possible.
Is this possible?
May be you want something like this?
public class Item<T>
{
public T Value => this.Values.Length > 0 ? this.Values[0] : default(T);
public T[] Values { get; set; }
}
The class has an array of values and a single value. There are some implementations like this, for example, when you select files with OpenFileDialog: you have a list of files (for MultiSelect case) and also a single SelectedFile. My answer is focused with this in mind. If you need another thing, give more information.
UPDATE
You can update previous class in this way:
public class Item<T>
{
public T Value => this.Values.Length > 0 ? this.Values[0] : default(T);
public T[] Values { get; set; }
public T this[int index] => this.Values[index];
public static implicit operator Item<T>(T value)
{
return new Item<T> { Values = new[] { value } };
}
public static implicit operator Item<T>(List<T> values)
{
return new Item<T> { Values = values.ToArray() };
}
public static implicit operator Item<T>(T[] values)
{
return new Item<T> { Values = values };
}
}
Example usage:
Item<string> item = "Item1";
string text = item.Value;
string sameText = item[0];
Item<string> items = new[] { "Item1", "Item2" };
string[] texts = item.Values;
string item1 = item[0];
string item2 = item[1];
You can create an instance using a simple object or an array. You can access to the first value using Value property and to all items using Values. Or use the indexer property to access to any item.
In C# you need to know the type of the variable. It's difficult work in the same form of JavaScript. They are very different languages.

C# get if a property is any sort of list

I'm trying to make a text writer according to my classes properties in the following pattern:
MyClass
ID 1
Name MyName
AnotherProperty SomeValue
ThisIsAnotherClass
AnotherClassProperties 1
//Example class
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
public string AnotherProperty { get; set; }
public AnotherClass ThisIsAnotherClass { get; set; }
}
So I'm taking each property name, writing it, a blank space, then it's value (if there is any).
Now I'm trying to implement support for lists and anything array-like for something like this:
MyClass
ArrayTest
1
2
3
If it's a class, I'll have a recursive for the function so I can display all values inside the list/array in this pattern. (it's for a webservice)
My question is, how can I find if a specific property is something list-able?
I've tried:
Type type = myObject.GetType();
PropertyInfo[] properties = type.GetProperties();
for(int i = 0; i < properties.Length; i++)
{
if(properties[i].PropertyType.IsGeneric) //Possible List/Collection/Dictionary
{
//Here is my issue
Type subType = properties[i].PropertyType.GetGenericTypeDefinition();
bool isAssignable = subType.IsAssignableFrom(typeof(ICollection<>)); //Always false
bool isSubclass = subType.IsSubclassOf(typeof(ICollection<>)); //Always false
//How can I figure if it inherits ICollection/IEnumerable so I can use it's interface to loop through it's elements?
}
else if(properties[i].PropertyType.IsArray) //Array
{
}
else if(properties[i].PropertyType.IsClass && !properties[i].PropertyType.Equals(typeof(String)))
{
//Non-string Subclasses, recursive here
}
else
{
//Value types, write the text + value
}
}
Like mentioned in the comments: use Json as a way to format objects, it will save a lot of time.
If you have reason not to do this, you can check if the type is enumerable: This also covers the Type.IsArray case.
typeof(IEnumerable).IsAssignableFrom(properties[i].PropertyType)
As an added notice of caution: maybe you do not want to enumerate String and byte[] type objects.

Store object string reference

I think this question was asked many times in C# but my problem is maybe more solvable.
I have an object value as a string : myobject.value and I want to store this value in a queue (or anything else) to access it later. Is it possible?
I read a lot of posts saying that it is not possible to store a ref to a string.
I don't see any solution to store a ref to my string value myobject.value and change it later.
Any Ideas ?
If you want to store a reference to a string that people can see and share the changes of you will need to wrap it in a class:
public class Wrapper<T>
{
public T Item { get; set; }
}
Then people use this instead of a string:
class MyClass
{
public Wrapped<string> SharedString { get; set; }
}
var c1 = new MyClass();
var c2 = new MyClass();
var s = new Wrapped<string> { Item = "Hello" };
c1.SharedString = s;
c2.SharedString = s;
c1.SharedString.Item = "World";
Console.Writeline(c2.SharedString.Item);
Even though strings are reference types, they are immutable so changes need to the "copied" around. Sharing is this way doesn't change the immutability, it just centrally holds one copy of a string that everyone is looking at via their reference to Wrapped<string>.
You can take the Wrapped<T> class further and give it implicit cast support to and from T, for a little syntactic sugar:
public static implicit operator T(Wrapped<T> wrapper)
{
return wrapper.Item;
}
public static implicit operator Wrapped<T>(T item)
{
return new Wrapped<T> { Item = item };
}
Wrapped<int> i = 2;
int j = i;
// Careful, this is a re-assignment of s, not a change of s.Item.
Wrapped<string> s = "Hello";
If I understand you right, and myobject.value is a string, than you can certainly store a collection representing those values
List<string> strLst = new List<string>();
strLst.Add(`myobject.value`);
No, you can't store a reference as a string, nor should you have to, but you can make a string(or xml) that represents all the information about your object, if you really need to.

A Good C# Collection

What's a good collection in C# to store the data below:
I have check boxes that bring in a subjectId, varnumber, varname, and title associated with each checkbox.
I need a collection that can be any size, something like ArrayList maybe with maybe:
list[i][subjectid] = x;
list[i][varnumber] = x;
list[i][varname] = x;
list[i][title] = x;
Any good ideas?
A List<Mumble> where Mumble is a little helper class that stores the properties.
List<Mumble> list = new List<Mumble>();
...
var foo = new Mumble(subjectid);
foo.varnumber = bar;
...
list.Add(foo);
,..
list[i].varname = "something else";
public Class MyFields
{
public int SubjectID { get; set; }
public int VarNumber { get; set; }
public string VarName { get; set; }
public string Title { get; set; }
}
var myList = new List<MyFields>();
To access a member:
var myVarName = myList[i].VarName;
A generic list, List<YourClass> would be great - where YourClass has properties of subjectid, varnumber etc.
You'd likely want to use a two-dimensional array for this, and allocate positions in the second dimension of the array for each of your values. For instance, list[i][0] would be the subjectid, list[i][1] would be varnumber, and so on.
Determining what collection, typically begins with what do you want to do with it?
If your only criteria is it can be anysize, then I would consider List<>
Since this is a Key, Value pair I would recommend you use a generic IDictionary based collection.
// Create a new dictionary of strings, with string keys,
// and access it through the IDictionary generic interface.
IDictionary<string, string> openWith =
new Dictionary<string, string>();
// Add some elements to the dictionary. There are no
// duplicate keys, but some of the values are duplicates.
openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");
As others have said, it looks like you'd be better creating a class to hold the values so that your list returns an object that contains all the data you need. While two-dimensional arrays can be useful, this doesn't look like one of those situations.
For more information about a better solution and why a two-dimensional array/list in this instance isn't a good idea you might want to read: Create a list of objects instead of many lists of values
If there's an outside chance that the order of [i] is not in a predictable order, or possibly has gaps, but you need to use it as a key:
public class Thing
{
int SubjectID { get; set; }
int VarNumber { get; set; }
string VarName { get; set; }
string Title { get; set; }
}
Dictionary<int, Thing> things = new Dictionary<int, Thing>();
dict.Add(i, thing);
Then to find a Thing:
var myThing = things[i];

C# Generics Efficiency, a better way to do this

Ok, lets say I have classes such as the following:
public class KPIObject<T> //<--This class where T is the following classes
{
public List<T> Data { get; set; }
public string Caption { get; set; }
}
public class KPICycleCountAccuracyData //<--There are 20 of these with different names and values
{
public string Facility { get; set; }
public string CCAdjustedCases { get; set; }
public string TotalCases { get; set; }
public string CCAdjustedPercent { get; set; }
}
Then I have:
public List<ReportData>> ProcessAccountReport(GetAccountReport request)
{
var data = new List<ReportData>();
ProcessKPI(data, request.KPICycleCountAccuracy, "KPICycleCountAccuracy"); //<-- 20 of these
return data;
}
Here is the ProcessKPI method:
private static void ProcessKPI<T>(List<ReportData> data, ICollection<KPIObject<T>> items, string name)
{
if (items == null || items.Count <= 0) return;
foreach (var item in items)
{
if (item.Data == null || item.Data.Count <= 0) continue;
var temp = new List<object>();
temp.AddRange((IEnumerable<object>)item.Data);
data.Add(new ReportData { Data = temp, Name = name, Title = item.Caption });
}
}
All of this works and compiles correctly, I am just wondering if this is the most efficient way of doing this.
Thanks.
EDIT
I changed process KPI to this:
private static void ProcessKPI<T>(ICollection<ReportData> data, ICollection<KPIObject<T>> items, string name)
{
if (items == null || items.Count <= 0) return;
foreach (var item in items.Where(item => item.Data != null && item.Data.Count > 0))
{
data.Add(new ReportData { Data = (IEnumerable<object>)item.Data, Name = name, Title = item.Caption });
}
}
Couple of comments
There is no need to make data a ref parameter in ProcessKPI. A ref parameter is only meaningful for a class type in C# if you actually assign to it. Here you're just modifying the object so ref doesn't by you anything except awkward call syntax
Even though Count is signed it won't ever return a negative value.
I would prefer (IEnumerable<object>)item.Data over the as IEnumerable<object> version. If the latter fails it will result in an ArgumentNullException when really it's a casting issue.
Speed
Assuming you are talking about computational efficiency (i.e. speed), there are two operations that you might be able to improve:
First, you create a copy of the item.Data in the temp variable. When you know that the resulting ReportData will never be modified, you may use the item.Data directly, forgoing the expensive copy operation.
data.Add(new ReportData {
Data = (IEnumerable<object>)item.Data,
Name = name,
Title = item.Caption });
Second, converting to IEnumerable<object> will probably cause unnecessary boxing/unboxing at a later point. See if it makes sense for your application to add a generic type parameter to ReportData, so you may instantiate it as new ReportData<KPIObject>(). That way the compiler may do a better job of optimizing the code.
Memory
By implementing your solution using continuations you may be able to process one ReportData element at a time instead of all at once, thereby reducing the memory footprint. Have a look at the yield statement to see how to impelement such an approach.
Other
For futher code quality improvements, JaredPar's answer offers some exellent advice.

Categories

Resources