I have a data row which I would like to make into an object array since to add it to my data table I need an object array. What I have made up to now is this...
data_table.Rows.Add(data_row.ToArray<object>());
but this does not work since this does not give an object array, at least thats what my compiler tells me
You can use ItemArray property on DataRow type.
object[] arr = data_row.ItemArray;
You could always make an extension method to DataRow like this:
public static class DataRowExtension
{
// Class for: Conversion to object[]
public static object[] ToObjectArray(this DataRow dataRow)
{
// Identifiers used are:
int columnCount = dataRow.Table.Columns.Count;
object[] objectArray = new object[columnCount];
// Check the row is not empty
if (columnCount == 0)
{
return null;
}
// Go through the row to add each element to the array
for (int i = 0; i < columnCount; i++)
{
objectArray[i] = dataRow[i];
}
// Return the object array
return objectArray;
}
}
Extension methods are great.
Related
I'm stuck trying to create array of arrays..
This is what i have for now, i would appreciate if someone could point me to right direction.
I have .txt file which has paths to images and each string has desired output separated with "|" like so:
":\\\img.png|1"
I'm trying to create array that has 2 columns and number of imagepaths as rows. Col 0 being a array of flattened rgb values of the image and col 1 being output as int.
I'm getting error from line Data[i][0] = Flat;
"Cannot implicitly convert type 'int[]' to 'int'"
It might be obvious to more experienced coders here but i cant wrap my head around this.
static int[][] CreateDataSet(string DatasetPath)
{
string[] Lines = File.ReadAllLines(DatasetPath);
int[][] Data = new int[Lines.GetUpperBound(0)][];
for (int i = 0; i <= Lines.GetUpperBound(0); i++)
{
Data[i] = new int[2];
string[] StringSplit = Lines[i].Split('|');
Data[i][1] = Convert.ToInt32(StringSplit[1]);
int[] Flat = FlattenArray(ImagetoArray(StringSplit[0]));
Data[i][0] = Flat;
}
return Data;
}
In an array, all elements must have same type (or at least must be assignable to a variable of the element's type).
You have two options.
The bad one: use array of objects.
object[][] data;
Now you can put everything in that array, but it will be slow (boxing of value types) and untyped (hard to use and to maintain).
Instead of a jagged array, use tuples.
(int[] FlattenedImage, int Output)[] data;
That looks a little bit weird, but it's actually very useful. It's strongly typed, it prevents boxing, and it uses nice and modern language features.
The big problem here is you have an int and an int[]. So the [0] index of Data must itself be an array, rather than merely an integer. And since the [0] and [1] subscripts are different types, you're really gonna need a completely different kind of data structure here.
Here's an example using Tuples:
static IEnumerable<(int, int[])> CreateDataSet(string DatasetPath)
{
var result = new List<(int, int[])> = new List<(int, int[])>();
foreach(string line in File.ReadLines(DatasetPath))
{
var lineData = line.Split('|');
yield return (int.Parse(linedata[1]), FlattenArray(ImageToArray(lineData[0])) );
}
}
or with linq:
static IEnumerable<(int, int[])> CreateDataSet(string DatasetPath)
{
return File.ReadLines(DatasetPath).Select(line => {
var data = line.Split('|');
return ( int.Parse(data[1]), FlattenArray(ImageToArray(data[0])) );
});
}
If you would create a class or a struct to containt the data it would look something like this:
class ImageData
{
public int[] FlatImate { get; }
public int Number { get; }
public ImageData(int[] flatImage, int number)
{
FlatImage = flatImage;
Number = number;
}
}
static ImageData[] CreateDataSet(string datasetPath)
{
string[] lines = File.ReadAllLines(datasetPath);
ImageData[] data = new ImageData[Lines.GetUpperBound(0)];
for (int i = 0; i <= lines.GetUpperBound(0); i++)
{
string[] stringSplit = lines[i].Split('|');
int number = Convert.ToInt32(stringSplit[1]);
int[] flat = FlattenArray(ImagetoArray(StringSplit[0]));
data[i] = new ImageData(flat, number);
}
return data;
}
I'm recieving UDP messages as byte arrays and depending on details in some of the first byte, I need to convert remaining bytes into a multitude of different possible arrangements of other data types.
These data types are primarily 'uint', 'ushort' and just 'byte' but can be in any order, and there can be any number of them. Each of these items will have a named variable.
I've tried using quite a few different options and am getting close, but feel that the methods I've created are not the best they could be. In places I've wanted to use 'sizeof' but don't want to mark the code as 'unsafe'. I've tried to use the 'params' keyword on the input, but this cannot be in conjunction with 'ref'. I've wanted to pass them with a maximum number of T1, T2, etc. using generics, but realised I can't enumerate these. I'm now passing an array of variables in conjunction with the 'ref' keyword, which means creating the array in memory but only the array gets updated with the changed and not the original variables.
byte[] message = new byte[] { }; //some byte array
ushort item1 = default(ushort);
byte item2 = default(byte);
var argumentArray = new object[] { item1, item2 };
ConvertArray(response, ref argumentArray);
private void ConvertArray(byte[] response, ref object[] items)
{
int index = 0;
for (int i = 0; i < items.Length; i++)
{
var item = items[i];
var itemType = item.GetType();
var itemSize = SizeOf(item.GetType());
if (itemSize == 0)
{
continue;
}
else if (itemSize == 1)
{
items[i] = response[index];
}
else
{
var method = typeof(BitConverter).GetMethod($"To{itemType.Name}");
var returned = method.Invoke(null, new object[] { response, index });
items[i] = Convert.ChangeType(returned, itemType);
}
index = index + itemSize;
}
}
private int SizeOf(Type type)
{
switch (type.Name)
{
case nameof(UInt16):
return 2;
case "Byte":
return 1;
default:
return 0;
}
}
So this is partially working in that 'argumentArray' is updating with the values from the 'ConvertArray' method, but I'm sure there is a neater way to do this using Types.
Ideally I wouldn't need to create the 'argumentArray' and just pass the items (e.g. Item1 and Item2) directly as arguments to the method.
I have a csv file with 8 columns, and I am trying to populate an object with 8 variables, each being a list to hold the columns in the csv file. Firstly, I am populating a DataTable with my csv data.
I am now trying to populate my object with the data from the DataTable
DataTable d = GetDataTableFromCSVFile(file);
CoolObject l = new CoolObject();
for (int i = 0; i < d.Rows.Count; i++)
{
l.column1[i] = d.Rows[i].Field<int>("column1"); <-- error here
}
And here is my CoolObject
public class CoolObject
{
public List<int> column1 { set; get; }
protected CoolObject()
{
column1 = new List<int>();
}
}
Unfortunately I am receiving an error on the highlighted line:
System.InvalidCastException: Specified cast is not valid
Why is this not allowed? How do I work around it?
Obviously you DataTable contains columns of type string, so do integer validation in GetDataTableFromCSVFile method, so consumers of this method don't need to worry about it.
Obviously you DataTable contains columns of type string, so do integer validation in GetDataTableFromCSVFile method, so consumers of this method don't need to worry about it.
private DataTable GetDataTableFromCSVFile()
{
var data = new DataTable();
data.Columns.Add("Column1", typeof(int));
// Read lines of file
// line is imaginery object which contains values of one row of csv data
foreach(var line in lines)
{
var row = data.NewRow();
int.TryParse(line.Column1Value, out int column1Value)
row.SetField("Column1", column1Value) // will set 0 if value is invalid
// other columns
}
return data;
}
Then another problem with your code, that you assugn new values to List<int> through index, where list is empty
l.column1[i] = d.Rows[i].Field<int>("column1");
Above line will throw exception because empty list doesn't have item on index i.
So you in the end your method will look
DataTable d = GetDataTableFromCSVFile(file);
CoolObject l = new CoolObject();
foreach (var row in d.Rows)
{
l.column1.Add(row.Field<int>("column1"));
}
In case you are using some third-party library for retrieving data from csv to DataTable - you can check if that library provide possibility to validate/convert string values to expected types in DataTable.
Sounds like someone didn't enter a number in one of the cells. You'll have to perform a validation check before reading the value.
for (int i = 0; i < d.Rows.Count; i++)
{
object o = d.rows[i]["column1"];
if (!o is int) continue;
l.column1[i] = (int)o;
}
Or perhaps it is a number but for some reason is coming through as a string. You could try it this way:
for (int i = 0; i < d.Rows.Count; i++)
{
int n;
bool ok = int.TryParse(d.rows[i]["column1"].ToString(), out n);
if (!ok) continue;
l.column1[i] = n;
}
I've got two arrays, and I need to put the second forward items to the other array using Array.Copy, but nothing happens, it just does not add anything.
Here's the code:
DataRow[] auxRows = rFComDataSet.TestStepNames
.Select("ScenarioName = '" + scenarioName + "'");
DataRow[] newRows = new DataRow[auxRows.Count()];
auxRows.CopyTo(newRows, 0);
foreach (DataRow row in newRows)
{
DataRow teste = this.rFComDataSet.TestStepNames.NewRow();
Array.Copy(row.ItemArray, 1, teste.ItemArray, 0, 4);
row["ScenarioName"] = newScenarioName;
this.rFComDataSet.TestStepNames.Rows.Add(row.ItemArray);
}
This behavior is the consequence of the implementation of the ItemArray property.
This is the code of the GET accessor
public object[] ItemArray
{
get
{
int num;
object[] objArray;
DataColumn column;
int num2;
num2 = this.GetDefaultRecord();
objArray = new object[this._columns.Count];
num = 0;
goto Label_0037;
Label_001C:
column = this._columns[num];
objArray[num] = column[num2];
num += 1;
Label_0037:
if (num < ((int) objArray.Length))
{
goto Label_001C;
}
return objArray;
}
}
As you can see calling DataRow.ItemArray returns a new object array where the values from the underlyng row are copied to.
When you use Array.Copy you are setting values in this array not in
the underlying values of the DataRow. So your row remains with the null values
A possible workaround is the following (NOT TESTED)
object[] itemArray = new object[this.rFComDataSet.TestStepNames.Columns.Count];
Array.Copy(row.ItemArray, 1, itemArray, 0, 4);
this.rFComDataSet.TestStepNames.Rows.Add(itemArray);
In this way we force the underlying values of new row created by Rows.Add to be the value of the object array created separately
There are a couple of things to take note however. Your call auxRows.CopyTo(newRows, 0); doesn't create a new row, it just copy all the rows reference to the new array, but they points at the same data, so changing anything in newRows change the corresponding row in auxRows.
Finally it is not clear why you have all this work to copy the row and then add to the TestStepNames table the same row from the foreach loop
Just skip Copy and do like:
DataRow teste = this.rFComDataSet.TestStepNames.NewRow();
teste.ItemArray = row.ItemArray;
row.ItemArray will create a new object for you.
I have moved from C to C#.
I have a function which accepts an array. I want to pass one dimension of a Two Dimensional array to this function.
C Code would be:-
void array_processing(int * param);
void main()
{
int Client_ID[3][50];
/* Some
Processing
which fills
this array */
array_processing(&Client_ID[1]);
}
Now, When I want to do same in C#, How can I pass this array?
Function defination will look like:-
private void array_processing(ref int[] param);
and Array would be declared as :-
int[,] Client_ID = new int[3,50];
Now How can I pass Client_ID[1] to the function array_processing()??
By doing array_processing ( ref Client_ID[1]) shouts as "Wrong Number of Indices"!
You can't really do that. C# is less outgoing about its arrays, and prevents you from doing C-like manipulations. This is a good thing.
You have various options:
Create a 1D array and copy your 2D row to it.
Use a jagged array - an array of arrays, which is more like what C lets you do.
Have an array_processing overload that takes a 2D array and a row number.
If you really want to access a 2D row as a 1D array, you should create a 'RowProxy' class that will implement the IList interface and let you access just one row:
class RowProxy<T>: IList<T>
{
public RowProxy(T[,] source, int row)
{
_source = source;
_row = row;
}
public T this[int col]
{
get { return _source[_row, col]; }
set { _source[_row, col] = value; }
}
private T[,] _source;
private int _row;
// Implement the rest of the IList interface
}
Use a lambda expression that will lose the array semantics, but is rather cool:
var ClientId = ...;
var row_5_accessor = (c=>ClientId[5, c]);
You can use row_5_accessor as a function, row_5_accessor(3) will give you ClientId[5, 3]
You can use a jagged array
// Initialize jagged array
int[][] clientID = new int[3][];
for (int i=0; i<clientId.Length; i++)
{
clientId[i] = new int[50];
}
array_processing(ref clientId[1]);
And your method:
private void array_processing(ref int[] subArray);
Just declare method
private void ParseArray(int[,] ar)
{
// Some work...
}
UDP: Code format
A primitive way would be:
var dimNumber = 1;
int[] oneDimension = new int[50];
for(var i=0; i<50; i++)
{
oneDimension[i] = Client_ID[dimNumber][i];
}
array_processing ( ref oneDimension);
I would suggest using Lambda expressions like in the way 5 of zmbq's answer.
You could declare you array as
int[][] Client_ID = new[] { new int[50], new int[50], new int[50] };
and then you can pass it to your array_processing function
array_processing(ref Clinet_ID[1]);
Sorry for miss of my pen.
Late to the conversation, but here is a jagged array example to do this:
string[][] rows = GetStringArray(values);
string[] row = rows[0];
You would set up your jagged array something like:
// rowCount from runtime data
stringArray = new string[rowCount][];
for (int index = 0; index < rowCount; index++)
{
// columnCount from runtime data
stringArray[index] = new string[columnCount];
for (int index2 = 0; index2 < columnCount; index2++)
{
// value from runtime data
stringArray[index][index2] = value;
}
}