C# List of splitted strings - c#

I have the following problem.
I have these strings with whitespace between them.
"+name:string" "+age:int"
I split them with this code:
List<string> stringValueList = new List<string>();
stringValueList = System.Text.RegularExpressions.Regex.Split(stringValue, #"\s{2,}").ToList<string>();
now the elements of List looks like this
"+name:string"
"+age:int"
Now I want to split these strings and create Objects.
This looks like this:
// Storing the created objects in a List of objects
List<myObject> objectList = new List<myObject>();
for(i = 1; i < stringValueList.Count ; i+=2)
{
myObject object = new myObject();
object.modifier = '+';
object.name = stringValueList[i-1].Trim('+'); // out of the example the object.name should be "name"
object.type = stringValueList[i]; // out of the example the object.type value should "string"
objectList.Add(object);
}
At the end I should get two objects with these values:
List<myObject> objectList{ myObject object1{modifier = '+' , name ="name" , type="string"}, myObject object2{modifier='+', name="age" type="int"}}
But my result looks like this:
List<myObject> objectList {myObject object1 {modifier='+', name="name:string" type="+age:int"}}
So instead of getting 2 Objects, I am getting 1 Object. It puts both strings into the elements of the first object.
Can anyone help me out? I guess my problem is in the for loop because i-1 value is the first string in the List and i is the second string but I cant change this.

I guess my problem is in the for loop because i-1 value is the first string in the List and i is the second string but I cant change this.
I don't know why you do i += 2, because apparently you want to split each string in two again. So just have to change that.
Use foreach(), and inside your loop, split your string again:
foreach (var stringValue in stringValueList)
{
myObject object = new myObject();
var kvp = stringValue.Split(':');
object.modifier = '+';
object.name = kvp[0].Trim('+');
object.type = kvp[1];
objectList.Add(object);
}
Of course this code assumes your inputs are always valid; you'd have to add some boundary checks to make it more robust.

Alternatively, you could expand your Regex formula to do the whole thing in one go.
For example, with (?<=")[+](.*?):(.*?)(?="), all you'd have to do is assign the matched group values.
foreach (Match m in Regex.Matches(stringValue, "(?<=\")[+](.*?):(.*?)(?=\")"))
{
myObject obj = new myObject
{
modifier = '+',
name = m.Groups[1].Value,
type = m.Groups[2].Value
};
objectList.Add(obj);
}

It's interesting to see how others approach a problem. I would have done something like this:
public class MyObject
{
public char Modifier { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public static IEnumerable<MyObject> Parse(string str)
{
return str
.Split(' ')
.Where(s => string.IsNullOrEmpty(s) == false)
.ToList()
.ForEach(i =>
{
var sections = i.Remove(0, 1).Split(':');
return new MyObject()
{
Modifier = i[0],
Name = sections[0],
Type = sections[1]
};
});
}
}

Related

Can I turn an array of Strings into an array of substring arrays?

I have a text file that contains product information on each line, in the form of "productCode,productName,amountInStock,etc.."
I've used File.ReadAllLines to store each line as an element in an array, and now I'm using those strings to assign values to a list of product structs.
Here is the code being used to split those strings from the array into substrings:
foreach (String line in readProducts)
{
productData = line.Split(',');
readProducts[foreachCount] = productData;
foreachCount++;
}
Which gives me this error in Visual Studio:
Cannot implicitly convert type 'string[]' to 'string'
What would be the best way to accomplish this task, assuming that I must use structs rather than classes?
Use this way
List<string[]> readProducts = new List<string[]>();
foreach (String line in readProducts)
{
productData = line.Split(',');
readProducts.Add(productData);
}
Here is a better option for you:
Let product be the class, contains properties such as productCode, productName,amountInStock,etc.. as you mentioned in the question. you can create a list of product and directly assign the input values to the list as like the following:
string path="path here"
List<product> ProductList = new List<product>();
foreach (string line in File.ReadAllLines(path))
{
string[] productDetails = line.Split(',');
ProductList.Add(new product() { productCode = productDetails[0], productName = productDetails[1] });
}
Where the Product class looks like:
public class product
{
public string productCode { get; set; }
public string productName { get; set; }
// rest of properties
}
You can use Select to project to a new collection:
var allItems = readProducts.Select(line => line.Split(',')); // collection of string arrays
or to project to a new type:
var allProducts = readProducts.Select(line => line.Split(',')) // collection of string arrays
.Select(array => new Product {
productCode = array[0],
productName = array[1],
amountInStock = array[2],
// etc.
}); // collection of Products
Using System and jagged arrays, I was able to solve the problem. Here is the code used in the working program.
int i = 0;
String[][] allProducts = new String[readProducts.Length][];
var parsedProduct = readProducts.Select(item => item.Split(','));
foreach (var item in parsedProduct)
{
allProducts[i] = item;
i++;
}
allProducts[][] is a jagged array. Otherwise known as an Array of Arrays.
parsedProduct is similar to the jagged array, each element contains another array with the substrings extracted from the current line of readProduct as their elements.
allProducts's elements are assigned the contents of parsedProducts's elements by the foreach loop.
As far as I can tell, there isn't any way to cut out the middle man and just use Select() on readProducts directly. But I could be wrong.

How to combine two c# objects using Linq

I am trying to combine two LIKE objects together and remove duplicates.
Tried this
This didn't work
Here is my object [simple]
public class LabelItem
{
public string LabelName { get; set; }
public string LabelValue { get; set; }
}
my data call returns the same object type
public static List<LabelItem> ReturnControlLabelList(Enums.LanguageType languageType, string labelList = "")
I pass this to the method
string[] LABELLIST = new string[] { "foxLabel", "commonLabel" };
var helper = new LabelHelper(, LABELLIST);
this is where I get null
public LabelHelper(Enums.LanguageType languageType, string[] labelListName)
{
if (labelListName != null)
{
List<LabelItem> labels = new List<LabelItem>();
this.LabelList = new List<LabelItem>();
foreach (var name in labelListName)
{
labels = DBCommon.ReturnControlLabelList(languageType, name);
this.LabelList.Concat(labels).Distinct().ToList();
}
}
else
{
this.LabelList = null;
}
}
public List<LabelItem> LabelList { get; private set; }
The concat is not working. I keep getting count 0 for labels and I can see the returns come back with 275 and 125 in the for loop.
Thanks in advance for the help.
Still having an issue
I want to use the suggestion from below but am still struggling.
The string[] passed in will get two lists of labelitems that are not unique when joined together in the loop. I need the distinct of the multiple lists returned in this.LabelList.
I got it to work with this but...I'm sure it's crazy inefficient.
Thanks for the help.
this.LabelList = new List<LabelItem>();
foreach (var name in labelListName)
{
var ret = DBCommon.ReturnControlLabelList(languageType, name);
this.LabelList = this.LabelList.Concat(ret).Distinct().ToList();
}
var distinctList = this.LabelList.GroupBy(x => new { x.LabelName, x.LabelValue })
.Select(x => x.FirstOrDefault());
this.LabelList = new List<LabelItem>();
foreach (var item in distinctList)
{
this.LabelList.Add(item);
Debug.WriteLine(item.LabelName + ' ' + item.LabelValue);
}
}
this.LabelList.Concat(labels).Distinct().ToList(); without assigning it to something doesn't make much sense. LINQ query does not modify the source collection, it returns a new one, so you'd have to assign it back to this.LabelList if you want it to get updated:
this.LabelList = this.LabelList.Concat(labels).Distinct().ToList();
You should be aware, that it's highly inefficient solution, and you should go with something based on SelectMany:
this.LabelList
= labelListName.SelectMany(name => DBCommon.ReturnControlLabelList(languageType, name)
.Distinct()
.ToList();
Concat and most other linq methods return an IEnumerable which you then need to do something with. It will not change your existing list so you need to just assign it with:
this.LabelList = this.LabelList.Concat(labels).Distinct().ToList();

C#: LINQ query with split and parsing

I have an object with a String field containing a comma separated list of integers in it. I'm trying to use LINQ to retrieve the ones that have a specific number in the list.
Here's my approach
from p in source
where (p.Keywords.Split(',').something.Contains(val))
select p;
Where p.Keywords is the field to split.
I've seen the following in the net but just doesn't compile:
from p in source
where (p.Keywords.Split(',').Select(x=>x.Trim()).Contains(val))
select p;
I'm a LINQ newbie, but had success with simpler queries.
Update:
Looks like I was missing some details:
source is a List containing the object with the field Keywords with strings like 1,2,4,7
Error I get is about x not being defined.
Here's an example of selecting numbers that are greater than 3:
string str = "1,2,3,4,5,6,7,8";
var numbers = str.Split(',').Select(int.Parse).Where(num => num > 3); // 4,5,6,7,8
If you have a list then change the Where clause:
string str = "1,2,3,4,5,6,7,8";
List<int> relevantNums = new List<int>{5,6,7};
var numbers = str.Split(',').Select(int.Parse).Where(num => relevantNums.Contains(num)); // 5,6,7
If you are not looking for number but for strings then:
string str = "1,2,3,4,5,6,7,8";
List<string> relevantNumsStr = new List<string>{"5","6","7"};
var numbers = str.Split(',').Where(numStr => relevantNumsStr.Contains(numStr)); // 5,6,7
Here is an example of how you can achieve this. For simplicity I did to string on the number to check for, but you get the point.
// class to mimic what you structure
public class MyObj
{
public string MyStr{get;set;}
}
//method
void Method()
{
var myObj = new List <MyObj>
{
new MyObj{ MyStr="1,2,3,4,5"},
new MyObj{ MyStr="9,2,3,4,5"}
};
var num =9;
var searchResults = from obj in myObj
where !string.IsNullOrEmpty(obj.MyStr) &&
obj.MyStr.Split(new []{','})
.Contains(num.ToString())
select obj;
foreach(var item in searchResults)
Console.WriteLine(item.MyStr);
}
Thanks for all the answers, although not in the right language they led me to the answer:
from p in source where (p.Keywords.Split(',').Contains(val.ToString())) select p;
Where val is the number I'm looking for.

Load data from txt file to comboBox

Hi i have this structure of txt file:
Lukas 1
Zdenek 3
Martin 2
Kate 1
And i need load this data...the name i need load to comboBox...and when i choose from ComboBox for example Lukas, i need to save Name Lukas to variable Name and number 1 to variable Number...
It is possible?
I have this code now...
using (StreamReader reader = new StreamReader(#"C:\Us...nka\example.txt"))
{
string data = "";
data = reader.ReadToEnd().Trim();
}
But i need read separately Name and separately Number...Have you any ideas? Thanks..
You can use File.ReadLines and String.Split:
var lines = File.ReadLines(#"C:\Us...nka\example.txt");
var data = lines.Select(l => l.Split());
I would use a class to store both properties:
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
Now you can load the persons in a loop or with LINQ:
List<Person> allPersons = data
.Where(arr => arr.Length >= 2 && arr[1].Trim().All(Char.IsDigit))
.Select(arr => new Person
{
PersonName = arr[0].Trim(),
PersonID = int.Parse(arr[1].Trim())
})
.ToList();
Edit:
Yes thanks...but i cant load PersonsName to combobox
You can use a BindingSource for the ComboBox. Then set the DisplayMember and ValueMember properties accordingly:
var bindingSourcePersons = new BindingSource();
bindingSourcePersons.DataSource = allPersons;
personComboBox.DataSource = bindingSourcePersons.DataSource;
personComboBox.ValueMember = "PersonID";
personComboBox.DisplayMember = "PersonName";
First create a class like this:
public class Person {
public string Name {get;set;}
public int Number {get;set;}
}
then you can use Linq to convert the string you read like this:
var people = data
.Split(new {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries)
.Select(d => new Person { Name = d.Split(' ')[0], Value = int.Parse(d.Split(' ')[1])})
.ToList();
Or better you could read your data line by line, like this:
var people = from l in File.ReadLines(#"C:\Us...nka\example.txt")
let parts = l.Split(' ')
select new Person {
Name = parts[0].Trim(),
Value = int.Parse(parts[1].Trim())
};
here is a pseudo:
while the reader is not EndOfStream
read current line
split the line that was just read into a string[] array, the separator being a space
first item in the array would be the name and the second item in the array would be the number.
then you add the item in the combo box. The combobox has an Items collection and an add method, which just takes a System.Object.
http://msdn.microsoft.com/en-us/library/aa983551(v=vs.71).aspx

Getting values from a list using foreach

I have list that have values like"
[0] = "{ id = ES10209005, views = 501 }"
[1] = "{ id = HYT0209005, views = 5678}"
[3] = "{ id = POI0209005, views = 4568}"
I would like to pass the values(id,views) to a method using a for each loop.
method(id,views)
Something like:
foreach (string v in updatereponse)
{
method()
}
How do I isolate each value(id,views) from each row in the list then pass it to the method?
The list contains just a bunch of strings, anything based on this to fix the problem would be just a workaround (e.g. string parsing). You should really switch to a strongly typed model, e.g. define a class ViewCount:
public class ViewCount
{
public string Id {get;set;}
public int Views {get;set;}
}
You can then use a List<ViewCount> populate the list:
List<ViewCount> viewcounts = new List<ViewCount>();
viewCounts.Add(new ViewCount() { Id = "ES10209005", Views = 501 });
Since each ViewCount instance has Id and Views properties you can now do the proper thing:
foreach (var item in updatereponse)
{
method(item.Id, item.Views);
}
If you are saving this data in a file, an alternative would be to use XML instead of custom strings, then you could use Linq to XML to populate a List<ViewCount>, e.g. using a simple XML like this:
<ViewCounts>
<ViewCount id="ES10209005" views="501" />
</ViewCounts>
You can then load your list:
XElement viewXml = XElement.Load("test.xml");
List<ViewCount> viewCounts = viewXml.Descendants("ViewCount")
.Select(x => new ViewCount()
{
Id = (string)x.Attribute("id"),
Views = (int)x.Attribute("views")
}).ToList();
foreach (string v in updateresponse)
{
var values = v.Split(",");
var id = values[0].Replace("{ id = ", "").Trim();
var view = values[1].Replace("views = ", "").("}","").Trim();
method(id, value);
}
Here's another way...you may want to add error checking:
String Data = "{ id = ES10209005, views = 501 }";
String[] Segments = Data.Split(new char[] { ' ', ',' });
string ID = Segments[3];
int views = int.Parse(Segments[7]);
Assuming the structure of your String is like you showed us always, this can work for you.
// First split id and views part.
String[] firstSplit = v.Split(',');
// Get the respected value for each part.
String id = firstSplit[0].Split('=')[1].Trim();
String views = firstSplit[1].Split('=')[1].Trim().Replace("}","");
You can use String methods to retrieve the items (use Split and SubString for example) or you can use a regular expression.
E.g.
var list = UpdateResponse[0].Split("=,} ") ;
will result in a list split by all these characters (including space).
Then check the correct indices to use (use a debugger for that). Then you get something like:
var id = list[5];
var views = list[8];
(note: check the indices 5 or 8, they are just a guess).

Categories

Resources