How can I convert IList<object> to string array? - c#

How can I convert IList objects to string array?
While working on the telegram bot, some difficulties arose. I get a IList<object> from google table with some information. I need to convert this IList<object> to an array of strings. How can I do this?
static void ReadBudgetTypes()
{
var range = $"{settingsSheet}!B3:B";
var request = service.Spreadsheets.Values.Get(SpreadsheetId, range);
var response = request.Execute();
var values = response.Values; // here i get list of objects from google table
if (values != null && values.Count > 0)
{
foreach (var row in values)
{
Console.WriteLine("{0}", row[0]);
}
}
else
{
Console.WriteLine("No data!");
}
}

Assuming cells may not be strings and may (or may not) have null values, you can print for each cell of the row:
// assumes ToString() gives a meaningful string
var listOfStrings = row.Select(x => x?.ToString()).ToList();
foreach(string cell in listOfStrings)
Console.WriteLine(cell);
or the whole row, joined by a separator
Console.WriteLine(string.Join(", ", row);
If you know the cells are strings you can just cast
var listOfStrings = row.Cast<string>().ToList();
// or
var listOfStrings = row.Select(x => (string)x).ToList();
and then repeat either of the above (loop or string.Join).
If items could be null,
var listOfStrings = row.Select(x => (x ?? (object)"").ToString()).ToList();

you can try this:
var tempList=List<string>();
string[] arrayList=null;
if (values != null && values.Count > 0)
{
foreach (var row in values)
{
tempList.Add(row[0]);
}
arrayList=tempList.ToArray();
}

Try something like this:
IList<object> list = new List<object>(){ "something", "something else" };
string[] array = list.Select(item => (String)item).ToArray();

Related

Group multiple rows containing index and create list of custom objects for each index

I have got a List of strings (read from a file) in this order and format and need to convert into List of class.
1.0.1.0.1, Type: DateTime, Value: 06/03/2013 11:06:10
1.0.1.0.2, Type: DateTime, Value: 06/03/2014 11:06:10
1.0.1.0.3, Type: DateTime, Value: 06/03/2015 11:06:10
1.0.1.0.4, Type: DateTime, Value: 06/03/2016 11:06:10
1.0.1.0.5, Type: DateTime, Value: 06/03/2017 11:06:10
1.0.1.1.1, Type: Integer, Value: 1
1.0.1.1.2, Type: Integer, Value: 2
1.0.1.1.3, Type: Integer, Value: 3
1.0.0.1.4, Type: Integer, Value: 4
1.0.1.1.5, Type: Integer, Value: 5
1.0.1.2.1, Type: String, Value: Hello
1.0.1.2.2, Type: String, Value: Hello1
1.0.1.2.3, Type: String, Value: Hello2
1.0.1.2.4, Type: String, Value: Hello3
1.0.1.2.5, Type: String, Value: Hello4
Here is my class
public class MyData
{
public DateTime DateTime {get;set;}
public int Index {get;set;}
public string Value {get;set;}
}
Now What I wanted is to convert it into a list of C# class
Something like this...
List<MyData> myDataList = new List<MyData>();
MyData data1 = new MyData();
data1.DateTime = "06/03/2013 11:06:10";
data1.Index = 1;
data1.Value = "Hello";
myDataList.Add(data1);
MyData data2 = new MyData();
data2.DateTime = "06/03/2014 11:06:10";
data2.Index = 2;
data2.Value = "Hello1";
myDataList.Add(data2);
and so on..
This is what I have tried so far.
List<List<string>> allLists = lines
.Select(str => new { str, token = str.Split('.') })
.Where(x => x.token.Length >= 4)
.GroupBy(x => string.Concat(x.token.Take(4)))
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Do I really need to iterate or can I modify My LINQ to get me desired output ?
Here is my iteration.
foreach (var list in allLists)
{
MyData data = new MyData();
var splittedstring = list[0].Split(',').ToList();
if (splittedstring.Count == 3)
{
var valueData = splittedstring [2];
var indexof = valueData.IndexOf(':');
var value = valueData.Substring(indexof + 1);
// But Over here, how will get DateTime and Index ?
data.Value = value;
}
}
First, fix your GroupBy: string.Concat(x.token.Take(4)) may create uncertainties when dot-separated numbers are ambiguous. For example, 1.23.4.5 and 12.3.4.5 would both produce "12345" string. Use string.Join with some non-numeric separator instead:
.GroupBy(x => string.Join("|", x.token.Take(4)))
Now for the main part of your question an easy fix would be to add a static method that parses the list of three strings, and use it in your LINQ query:
List<MyData> dataList = lines
.Select(str => new { str, token = str.Split('.') })
.Where(x => x.token.Length >= 4)
.GroupBy(x => string.Concat(x.token.Take(4)))
.Select(g => g.Select(x => x.str).ToList())
.Where(list => list.Count == 3)
.Select(MyDataFromList)
.ToList();
...
private static MyData MyDataFromList(List<string> parts) {
if (parts.Count != 3) {
throw new ArgumentException(nameof(parts));
}
var byType = parts
.Select(ToTypeAndValue)
.ToDictionary(t => t.Item1, t => t.Item2)
return new MyData {
DateTime = DateTime.Parse(byType["DateTime"])
, Index = int.Parse(byType["Integer"])
, Value = byType["String"]
};
}
private static Tuple<string,string> ToTypeAndValue(string s) {
var tokens = s.Split(',');
if (tokens.Length != 3) return null;
var typeParts = tokens[1].Split(':');
if (typeParts.Length != 2 || typeParts[0] != "Type") return null;
var valueParts = tokens[2].Split(':');
if (valueParts.Length != 2 || valueParts[0] != "Value") return null;
return Tuple.Create(typeParts[1].Trim(), typeParts[2].Trim());
}
Note that the above code makes an assumption that the three types are unique (hence the use of Dictionary<string,string>). This is required, because the structure of your data provides no other way to tie the values to fields of MyData.
You can do this using regular expressions. It would look like:
public List<MyData> GetData(string str){
var regexDate = new Regex(#"\d\.\d\.\d\.\d\.(?<id>\d).*DateTime.*Value:\s*(?<val>.*)");
var regexInteger = new Regex(#"\d\.\d\.\d\.\d\.(?<id>\d).*Integer.*Value:\s*(?<val>.*)");
var regexString = new Regex(#"\d\.\d\.\d\.\d\.(?<id>\d).*String.*Value:\s*(?<val>.*)");
var dict = new Dictionary<int, MyData>();
foreach (Match myMatch in regexDate.Matches(str))
{
if (!myMatch.Success) continue;
var index = int.Parse(myMatch.Groups["id"].Value);
dict[index] = new MyData()
{
Index = index,
DateTime = DateTime.ParseExact(myMatch.Groups["val"].Value, "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture)
};
}
foreach (Match myMatch in regexInteger.Matches(str))
{
if (!myMatch.Success) continue;
var index = int.Parse(myMatch.Groups["id"].Value);
dict[index].Index = Int32.Parse(myMatch.Groups["val"].Value);
}
foreach (Match myMatch in regexString.Matches(str))
{
if (!myMatch.Success) continue;
var index = int.Parse(myMatch.Groups["id"].Value);
dict[index].Value = myMatch.Groups["val"].Value;
}
return dict.Values
}
Here is my solution to your problem. I have already tested it, you can test it to here: Raw To Custom List
string text = rawData;
//Raw Data Is the exact data you read from textfile without modifications.
List<MyData> myDataList = new List<MyData>();
string[] eElco = text.Split( new[] { Environment.NewLine }, StringSplitOptions.None );
var tmem = eElco.Count();
var eachP = tmem / 3;
List<string> unDefVal = new List<string>();
foreach (string rw in eElco)
{
String onlyVal = rw.Split(new[] { "Value: " } , StringSplitOptions.None)[1];
unDefVal.Add(onlyVal);
}
for (int i = 0; i < eachP; i++)
{
int ind = Int32.Parse(unDefVal[i + eachP]);
DateTime oDate = DateTime.ParseExact(unDefVal[i], "dd/MM/yyyy hh:mm:ss",System.Globalization.CultureInfo.InvariantCulture);
MyData data1 = new MyData();
data1.DateTime = oDate;
data1.Index = ind;
data1.Value = unDefVal[i + eachP + eachP];
myDataList.Add(data1);
Console.WriteLine("Val1 = {0}, Val2 = {1}, Val3 = {2}",
myDataList[i].Index,
myDataList[i].DateTime,
myDataList[i].Value);
}
Here is my solution, using Regex. It could be improved by providing a conditional regex match based on the matched type named group(string), but I think the concept is clearer this way, and the regex easier to work with. As it stands, the date format is not validated to be as OP wrote them, they are assumed to be as OP wrote them.
This solution is tolerant to some extra spaces and parameters containing commas, but intolerant to inexact matches, i.e. extra fields added or removed in the rows in the future, etc.
The idea is to first parse the rows to a more "friendly" format, and then group the friendly format by index and return the MyData rows by iterating each group (by index).
Regex r = new Regex(#"^(?<fieldName>(\d\.)+(?<index>\d*)), *Type: *(?<dataType>.*), *Value: (?<dataValue>.*)$");
public class MyData
{
public DateTime DateTime { get; set; }
public int Index { get; set; }
public string Value { get; set; }
}
class LogRow
{
public int Index { get; set; }
public string Type { get; set; }
public string Value { get; set; }
}
//In a parser I would rather not be too defensive, I let exceptions bubble up
IEnumerable<LogRow> ParseRows(IEnumerable<string> lines)
{
foreach (var line in lines)
{
var match = r.Matches(line).AsQueryable().Cast<Match>().Single();
yield return new LogRow()
{
Index = int.Parse(match.Groups["index"].Value),
Type = match.Groups["dataType"].Value,
Value = match.Groups["dataValue"].Value
};
}
}
IEnumerable<MyData> RowsToData(IEnumerable<LogRow> rows)
{
var byIndex = rows.GroupBy(b => b.Index).OrderBy(b=> b.Key);
//assume that rows exist for all MyData fields for a given index
foreach (var group in byIndex)
{
var rawRow = group.ToDictionary(g => g.Type, g => g);
var date = DateTime.ParseExact(rawRow["DateTime"].Value, "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture);
yield return new MyData() { Index = group.Key, DateTime = date, Value = rawRow["String"].Value };
}
}
Usage:
var myDataList = RowsToData(ParseRows(File.ReadAllLines("input.txt"))).ToList();
I'd just go for the manual approach... and since that list of integers at the start contains indices for the objects and for the properties, it'd only be logical to use these instead of the type strings.
Using a Dictionary, you can use that object-index to make a new object at the moment you find any of its properties, and store it using that index. And whenever you encounter another properties for the same index, you retrieve the object and fill in that property on it.
public static List<MyData> getObj(String[] lines)
{
Dictionary<Int32, MyData> myDataDict = new Dictionary<Int32, MyData>();
const String valueStart = "Value: ";
foreach (String line in lines)
{
String[] split = line.Split(',');
// Too many fail cases; I just ignore any line that stops matching at any point.
if (split.Length < 3)
continue;
String[] numData = split[0].Trim().Split('.');
if (numData.Length < 5)
continue;
// Using the 4th number as property identifier. Could also use the
// type string, but switch/case on a numeric value is more elegant.
Int32 prop;
if (!Int32.TryParse(numData[3], out prop))
continue;
// Object index, used to reference the objects in the Dictionary.
Int32 index;
if (!Int32.TryParse(numData[4], out index))
continue;
String typeDef = split[1].Trim();
String val = split[2].TrimStart();
if (!val.StartsWith(valueStart))
continue;
val = val.Substring(valueStart.Length);
MyData data;
if (myDataDict.ContainsKey(index))
data = myDataDict[index];
else
{
data = new MyData();
myDataDict.Add(index, data);
}
switch (prop)
{
case 0:
if (!"Type: DateTime".Equals(typeDef))
continue;
DateTime dateVal;
// Don't know if this date format is correct; adapt as needed.
if (!DateTime.TryParseExact(val, "dd/MM/yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out dateVal))
continue;
data.DateTime = dateVal;
break;
case 1:
if (!"Type: Integer".Equals(typeDef))
continue;
Int32 numVal;
if (!Int32.TryParse(val, out numVal))
continue;
data.Index = numVal;
break;
case 2:
if (!"Type: String".Equals(typeDef)) continue;
data.Value = val;
break;
}
}
return new List<MyData>(myDataDict.Values);
}

Converting/handling returned IList object

I'm having issues extracting values from a returned IList and populating a combobox in windows.forms. All items in the combobox are listed as System.object.
I have done some testing;
var retList = Services.Get<IWarehouseInfo>().ExecuteSPArray("sp_executesql", dict); <-- method that returns some values.
//Tries to extract value from retlist/which is a IList<object[]> collection.
var strList = (from o in retList select o.ToString()).ToList();
var strList2 = retList.OfType<string>().ToList();
var strList3 = retList.Cast<string>();
var strList4 = retList.Where(x => x != null).Select(x => x.ToString()).ToList(); //All these seem to result in system object.
var bub = strList.ElementAt(2).ToString();
var bob = strList4.ElementAt(2).ToString();
var kupa = strList.ToArray();
var kupo = kupa[2].ToString();
All these fail to extract anything useful.
I thank you all for any beeps given. My mistake was that I thought
that the returned values were in a list of objects. But the result was
an IEnumerable, so I did not check the correct vector.
I added an method extracting the values and returning it in desired format, in this case string.
private static List<string> ToListString(IEnumerable<object[]> inparam)
{
var custNums = new List<string>();
foreach (var row in inparam)
{
if (row[0] != null && row[0] != DBNull.Value)
custNums.Add(row[0].ToString());
}
return custNums;
}

Finding all identifiers containing part of the token

I know I can get a string from resources using
Resources.GetIdentifier(token, "string", ctx.ApplicationContext.PackageName)
(sorry, this is in C#, it's part of a Xamarin.Android project).
I know that if my elements are called foo_1, foo_2, foo_3, then I can iterate and grab the strings using something like
var myList = new List<string>();
for(var i = 0; i < 4; ++i)
{
var id = AppContent.GetIdentifier(token + i.ToString(), "string", "package_name");
if (id != 0)
myList.Add(AppContext.GetString(id));
}
My issue is that my token names all begin with "posn." (the posn can denote the position of anything, so you can have "posn.left_arm" and "posn.brokenose"). I want to be able to add to the list of posn elements, so I can't really store a list of the parts after the period. I can't use a string-array for this either (specific reason means I can't do this).
Is there a way that I can use something akin to "posn.*" in the getidentifer call to return the ids?
You can use some reflection foo to get what you want. It is not pretty at all but it works. The reflection stuff is based on https://gist.github.com/atsushieno/4e66da6e492dfb6c1dd0
private List<string> _stringNames;
private IEnumerable<int> GetIdentifiers(string contains)
{
if (_stringNames == null)
{
var eass = Assembly.GetExecutingAssembly();
Func<Assembly, Type> f = ass =>
ass.GetCustomAttributes(typeof(ResourceDesignerAttribute), true)
.OfType<ResourceDesignerAttribute>()
.Where(ca => ca.IsApplication)
.Select(ca => ass.GetType(ca.FullName))
.FirstOrDefault(ty => ty != null);
var t = f(eass) ??
AppDomain.CurrentDomain.GetAssemblies().Select(ass => f(ass)).FirstOrDefault(ty => ty != null);
if (t != null)
{
var strings = t.GetNestedTypes().FirstOrDefault(n => n.Name == "String");
if (strings != null)
{
var fields = strings.GetFields();
_stringNames = new List<string>();
foreach (var field in fields)
{
_stringNames.Add(field.Name);
}
}
}
}
if (_stringNames != null)
{
var names = _stringNames.Where(s => s.Contains(contains));
foreach (var name in names)
{
yield return Resources.GetIdentifier(name, "string", ComponentName.PackageName);
}
}
}
Then somewhere in your Activity you could do:
var ids = GetIdentifiers("action").ToList();
That will give you all the String Resources, which contain the string action.

How could I convert these foreach loops into a LINQ-expression?

I used ReSharper to inspect the code issues in my project and it notified me that the following loop could be converted into a LINQ-expression:
var dictionary = new Dictionary<string, string[]>
{
{ "400", new[] { "12345", "54321", "51423" } },
{ "500", new[] { "67890", "09876", "63727" } },
{ "600", new[] { "41713", "98234", "96547" } },
{ "700", new[] { "00000", "67990", "83752" } }
};
// ...
var targetValue = "41713";
foreach (string group in dictionary.Keys)
{
foreach (string name in dictionary[group])
{
if (name == targetValue)
return group;
}
}
return "User";
The loop basically checks the dictionary's values (string arrays) to see if targetValue belongs to any of them and returns the key of that array if found inside.
I tried doing the following, but clearly it just returns the value inside if its value is equivalent to targetValue.
var r = dictionary
.SelectMany(t => t.Value)
.FirstOrDefault(t => t == targetValue);
So you want to get the first key in the dictionary which string[]-value contains a given value?
var pairs = dictionary.Where(kv => kv.Value.Contains(myValue));
if (pairs.Any())
{
string group = pairs.First().Key;
}
or less readable but a little bit more efficient since it executes the query only once:
var pair = dictionary.FirstOrDefault(kv => kv.Value.Contains(myValue));
if (!pair.Equals(default(KeyValuePair<string, string[]>)))
{
string group = pair.Key;
}
last but not least another approach which is my favorite and also uses the "User"-default:
string group = dictionary.Where(kv => kv.Value.Contains(myValue))
.Select(kv=> kv.Key)
.DefaultIfEmpty("User")
.First();
var r = dictionary.FirstOrDefault(
x => x.Value.FirstOrDefault(y => y == myValue) != null);
This will also get the desired value back or null if it does not exist:
EDIT:
var result = dictionary.SkipWhile(n => !n.Value.Contains(myValue)).FirstOrDefault().Key;
//another way to get the key
//var result = dictionary.SingleOrDefault(n => n.Value.Contains(myValue)).Key;
if (result != null)
{
//do whatever with the result variable here
}

looping through an array of strings and converting to ints [duplicate]

This question already has answers here:
Change strings from one string to another in an array based on a condition
(8 answers)
Closed 9 years ago.
I have an array of strings, most of which are values "true" or "false" some are not and are to remain as are.
I wish to loop through the array and change any "true" or "false" to 1' or zero's
I have kind of started it, but am struggling with the syntax
string[] data = args.Trim().Split(',');
// Loop over strings
foreach (string s in data)
{
if(s == "true")
{
Convert.ToInt32(s)=1;????
}
else if(s == "false")
{
??????
}
}
please can someone offer some guidance
Perhaps with Linq:
string[] data = args.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => StringComparer.OrdinalIgnoreCase.Equals("true", s.Trim()) ? "1"
: StringComparer.OrdinalIgnoreCase.Equals("false", s.Trim()) ? "0" : s)
.ToArray();
Added the StringComparer.OrdinalIgnoreCase to show how you can ignore the case if desired.
Here's a demo with the sample string commented.
this,1,is,not,0
Write a function like this. You can then copy the "converted data" into a new List and return it as an array, or whatever.
public string[] GetData()
{
string[] data = args.Trim().Split(',');
List<string> returnData = new List<string>();
// Loop over strings
foreach (string s in data)
{
if(s == "true"){
returnData.Add("1");
}
else if(s == "false"){
returnData.Add("0");
}
else
returnData.Add(s);
}
return returnData.ToArray();
}
made a presumption you want an array of type string as you don't specify.
Other options as it's unclear what your going to do with the two types of data are to parse the values when you get them out, or split them up into two lists.
string[] rawDatas = GetData();
foreach(string rawData in rawDatas)
{
short iRawData;
if (Int16.TryParse(rawData, out iRawData))
{
if (iRawData == 1 || iRawData == 0)
{
//Add a bit
iRawData;
}
else
{
// add a string
rawData;
}
}
else
{
//add a string
rawData;
}
}
OR
public void GetData(out List<string> strings, out List<Int16> shorts)
{
string[] data = args.Trim().Split(',');
strings= new List<string>();
shorts = new List<Int16>();
// Loop over strings
foreach (string s in data)
{
if(s == "true"){
shorts.Add(1);
}
else if(s == "false"){
shorts.Add(0);
}
else
returnData.Add(s);
}
}
OR
add to an array of object, though this will need casting back on the other side, which is inefficent (see boxing and unboxing)
public object[] GetData()
{
string[] data = args.Trim().Split(',');
List<object> returnData = new List<object>();
// Loop over strings
foreach (string s in data)
{
if(s == "true"){
returnData.Add(1);
}
else if(s == "false"){
returnData.Add(0);
}
else
returnData.Add(s);
}
return returnData.ToArray();
}
if you just want to do 0 and 1 than
string[] data = args.Trim().Split(',');
int[] a = new int[data.length];
// Loop over strings
int count=0;
foreach (string s in data)
{
if(s == "true"){
int a[count] = 1;
}
else if(s == "false"){
int a[count]=0;
}
else
{
a[count] = Convert.ToInt32(s);
}
count++;
}
or with linq
var intlst = data.
Select(input => input == "true" ? 1 :
(input == "false" ? 0 : Convert.ToInt32(input)));
Try this:
var result = data.Select(input => input == "true" ? 1 : (input == "false")? 0 : -1);
You can do this by simple for loop:
List<string> newArgs = new List<string>();
for (int i = 0; i <= args.Length - 1; i++)
{
newArgs.Add(args[i] == "true" ? "1"
: args[i] == "false" ? "0"
: args[i]);
}
Or by using linq which will internally use extension object to iterate through the collection.
Well the problem that I can see you running into is holding the variables themselves without creating a new array. When you type the line
string[] data = args.Trim().Split(',');
you are declaring that the variables within that array are of type string. If you wish to convert them to integers you will need to create a new array of integers or undefined type. I think you are using C# which I believe the code for multiple types in an array is
object[]
In that case you would do something like
object[] data = args.Trim().Split(',');
foreach(string s in data){
if(s == "true"){
s = 1;
Convert.ToInt32(s);
}
else if(s == "false"){
s = 0;
Convert.ToInt32(s);
}
}

Categories