A little question for a simple LINQ request. This is my first time with LINQ and still not understand all mechanism.
My structure is something like this
List<string> baseData = new List<string>{"\"10\";\"Texte I need\";\"Texte\"",
"\"50\";\"Texte I need\";\"Texte\"",
"\"1000\";\"Texte I need\";\"Texte\"",
"\"100\";\"Texte I need\";\"Texte\""};
Each line of data is construct with field separator ";" and each field are encapsule with quote ".
I have another List Compose with value i have to find in my first list. And i have the Position in line i have to search. because "Texte I need" can be equal with value i am searching
List<string> valueINeedToFind = new List<string>{"50","100"};
char fieldSeparator = ';';
int fieldPositionInBaseDataForSearch = 0;
int fieldPositionInBaseDataToReturn = 1;
I made a first Linq to extract only Line interested me.
List<string> linesINeedInAllData = baseData.Where(Line => valueINeedToFind.Any(Line.Split(fieldSeparator)[fieldPositionInBaseDataForSearch].Trim('"').Contains)).ToList();
This first request Work Great and now i have only Data Line Interested me.
My problem is I don't want all the line But only a list of the value "Texte I need" in position FieldPositionInBaseDataToReturn.
I have to made another LINQ or can i modify my first to directly get what I need?
Since you will be using the split version of each line more than once, separate out the Split operation and then work on the resulting array:
List<string> linesINeedInAllData = baseData.Select(Line => Line.Split(fieldSeparator))
.Where(splitLine => valueINeedToFind.Any(splitLine[fieldPositionInBaseDataForSearch].Trim('"').Contains))
.Select(splitLine => splitLine[fieldPositionInBaseDataToReturn])
.ToList();
List<string> linesINeedInAllData = baseData.Where(Line => valueINeedToFind.Any(Line.Split(fieldSeparator)[fieldPositionInBaseDataForSearch].Trim('"').Equals)).ToList()
.Select(Line => Line.Split(fieldSeparator)[fieldPositionInBaseDataToReturn].Trim('"').ToList();
Related
I Have a column "OPERATOR_ID" which contain values as 'Condition#10', 'Condition#7', 'Condition#13'etc. I want to retrieve these values from the datatable then sort them based on '#(integer number)' and then convert these string values to lowercase.
I am able to successfully achieve it but from performance point I am using lot of memory and It can be done in a better way I feel.
Here is my code:
List<object> OPERATOR_ID_Values = new List<object>();
OPERATOR_ID_Values = dataSet.Tables[0].AsEnumerable().Select(r => r["OPERATOR_ID"]).ToList();
var OPERATOR_ID_Values_sort1 = OPERATOR_ID_Values.OrderBy(x => PadNumbers((string)x)).ToList();
var OPERATOR_ID_Values_sort = OPERATOR_ID_Values_sort1.Select(x => x.ToString().ToLower()).ToList();
public static string PadNumbers(string input)
{
return Regex.Replace(input, "[0-9]+", match => match.Value.PadLeft(10, '0'));
}
"OPERATOR_ID_Values_sort" gives me the desired result.
How Can I make it better from performance point or reduce the LINQ lines. I tried using "ToString().ToLower()" with OrderBy but I dint get the desired result.
Thanks :)
This is off the top of my head. I'll test and confirm then edit if required:
var operatorIds = (from row in dataSet.Tables[0].AsEnumerable()
let parts = row.Field<string>("OPERATOR_ID").Split('#')
order by Convert.ToInt32(parts[1])
select $"{parts[0].ToLower()}#{parts[1]}").ToList();
I'm trying to create a concise LINQ query to split a CSV file and convert to a XML file from a array of columns I have gleaned of a XSD file.
Its all working good. Except I just can't get the Counter to reset back to Zero after each row. It should go 0,1,2,3,4 then 0,1,2,3,4 but its going 0,1,2,3,4 then 5,6,7,8,9.
I'm new to LINQ so hopefully this is simple for someone with a bit of experience, thanks!
string[] columns = {"COL1","COL2","COL3","COL4","COL5"};
int Counter = 0;
XElement cust = new XElement("Root",
from str in source.Skip(1)
let fields = str.Split(',')
select new XElement("Records",
from c in columns
select new XElement(c, fields[Counter++])
)
);
That is not the way to do this. It is extremely bad practice to have side-effecting functions (like an incrementor) inside a LINQ select clause, particularly because of things like parallelization. If you were doing this manually with a foreach, I might be tempted to just suggest use of a mod:
fields[(Counter++) % fields.Length]
But even that would still be a little weird.
This is a more acceptable way, which uses the Zip method to find column names by matching them up by index.
string[] columns = {"COL1","COL2","COL3","COL4","COL5"};
var rows = source.Skip(1)
.Select(c => columns.Zip(c.Split(','),
(column, value) => new
{
Column = column,
Value = value
});
var elements = rows.Select(c => new XElement("Records",
c.Select(x => new XElement(c.Column, c.Value))));
return new XElement("Root", elements);
That all said, it's important to note that this is not currently generalizable, and will fail when the columns contain quoted values with commas in them. You might want to look into third party libraries. I've had luck with CsvHelper myself.
Hello I'm new to linq and lambda
I have two lists
fl.LocalOpenFiles ...
List<string> f....
there is a property (string) for example taking index 0
fl.LocalOpenFiles[0].Path
i wanted to select all from the first list fl.LocalOpenFiles where fl.LocalOpenFiles.Path starts with a string from the List<string> f
I finally got this...
List<LocalOpenFile> lof = new List<LocalOpenFile>();
lof = fl.LocalOpenFiles.Join(
folders,
first => first.Path,
second => second,
(first, second) => first)
.ToList();
But its just selecting folders that meet the requirement first.Path == second and i couldnt find a way to get the data that i want which is something meeting this "braindump" requirement:
f[<any>] == fl.LocalOpenFiles[<any>].Path.Substring(0, f[<any>].Length)
Another Example...
List<string> f = new List<string>{ "abc", "def" };
List<LocalOpenFile> lof = new List<LocalOpenFile>{
new LocalOpenFile("abc"),
new LocalOpenFile("abcc"),
new LocalOpenFile("abdd"),
new LocalOpenFile("defxsldf"),)}
// Result should be
// abc
// abcc
// defxsldf
I hope i explained it in a understandable way :)
Thank you for your help
Do you mean something like this :
List<LocalOpenFile> result =
lof.Where(file => f.Any(prefix => file.Path.StartsWith(prefix)))
.ToList();
You can use a regular where instead of a join, which will give you more straight forward control over the selection criteria;
var result =
from file in lof
from prefix in f
where file.Path.StartsWith(prefix)
select file.Path; // ...or just file if you want the LocalOpenFile objects
Note that a file matching multiple prefixes may show up more than once. If that is a problem, you can just add a call to Distinct to eliminate duplicates.
EDIT:
If you - as it seems in this case - only want to know the matching path and not the prefix it matches (ie you only want data from one collection as in this case), I'd go for #har07's Any solution instead.
Say I have List<string> FontStyle containing the following
"a0.png",
"b0.png",
"b1.png",
"b2.png",
"b3.png",
"c0.png",
"c1.png",
"d0.png",
"d1.png",
"d2.png"
I want to randomly select a string from the list with its first character matches a certain character. For example if the character is c. The method will returns either c0.png or c1.png randomly.
How do I do this using LINQ?
This should do the trick:
var random = new Random();
var list = new List<string> {
"a0.png",
"b0.png",
"b1.png",
"b2.png",
"b3.png",
"c0.png",
"c1.png",
"d0.png",
"d1.png",
"d2.png"
};
var startingChar = "d";
var filteredList = list.Where(s => s.StartsWith(startingChar)).ToList();
Console.WriteLine(filteredList.Count);
int index = random.Next(filteredList.Count);
Console.WriteLine(index);
var font = filteredList[index];
Console.WriteLine(font);
but the problem with the entire solution is that the smaller the resulting filtered list is the less likely you are to get really random values. The Random class works much better on much larger constraints - so just keep that in mind.
Random random = ...;
var itemsStartingWithC = input
.Where(x => x.StartsWith("c"))
.ToList();
var randomItemStartingWithC =
itemsStartingWithC.ElementAt(random.Next(0, itemsStartingWithC.Count()));
The call to ToList isn't strictly necessary, but results in faster code in this instance. Without it, Count() will fully enumerate and ElementAt will need to enumerate to the randomly selected index.
I have this code which will sometimes just have one string in it and sometimes have two. Basically when it has two strings I want to be able to select the second string as at the moment it is selecting the first string in the list.
Here is my code:
List<string> workGroupIdStringValues = new List<string>();
workGroupIdStringValues = (List<string>)session["WorkGroupIds"];
List<Guid> workGroupIds = workGroupIdStringValues.ConvertAll<Guid>(workGroupIdStringValue => new Guid(workGroupIdStringValue));
So "workGroupIdStringValues" will sometimes have a second string, how can I select the second and not the first when there is two strings. Is it possible, if so how?
Thanks
Use LINQ's workGroupIdStringValues.Last() to discard all strings but the last one; will work fine if there's just one string.
Update: And then of course you have to adapt the code somewhat:
var workGroupId = new Guid(((List<string>)session["WorkGroupIds"]).Last());
How about
string workGroupIdStringValue = ((List<string>)session["WorkGroupIds"]).Last();
Instead of
List<Guid> workGroupIds = workGroupIdStringValues.ConvertAll<Guid>(workGroupIdStringValue => new Guid(workGroupIdStringValue));
You can do following
Guid workGroupId = new Guid(workGroupIdStringValues[workGroupIdStringValues.Count-1]));
There are a few ways of doing this, I think the best is to do
workGroupIdStringValues.Count will give you the total amount of objects, and
workGroupIdStringValues[1] will return the second entry in your list.
So something like
if(workGroupIdStringValues.Count() > 1)
mystring = workGroupIdStringValues[1];
else
mystring = workGroupIdStringValues[0];