How do you query a List<string[]> to get the index of the arrays having matches on their sub-arrays and get a return of type System.Collections.Generic.IEnumerable<string[]> ?
EDIT:
I have this:
string[] report = File.ReadAllLines(#".\REPORT.TXT").AsQueryable().Where(s
=> s.StartsWith(".|")).ToArray();
List<string[]> mylist = new List<string[]>();
foreach (string line in report)
{
string[] rows = line.Split('|');
mylist.Add(rows);
}
and I what to get the the mylist indexes where rows[5] == "foo"
For the original question:
list.Where(array => array.Any(item => item == match))
For the updated one:
result = Enumerable.Range(0, list.Count - 1).Where(i => list[i][5] == "foo");
You do actually need to check if the array has at least 6 items as well:
i => list[i].Length > 5 && list[i][5] == "foo"
You mean something like this?
var haystacks = new List<string[]>();
haystacks.Add(new string[] { "abc", "def", "ghi" });
haystacks.Add(new string[] { "abc", "ghi" });
haystacks.Add(new string[] { "def" });
string needle = "def";
var haystacksWithNeedle = haystacks
.Where(haystack => Array.IndexOf(haystack, needle) != -1);
Related
Hi I am in a bit of an issue I keep getting this error:
Index was outside the bounds of the array.
var d = File.ReadAllLines(#"studentsFile.txt");
var t = d.Where(g => g.Contains("Student Name"));
string[] splited;
foreach (var item in t)
{
splited = item.Split(new string[] { "Student Name:" }, StringSplitOptions.None);
cbListStudents.Items.Add(splited[1]);
}
The above works perfectly but the code below does not:
var cour = File.ReadAllLines(#"CourseFile.txt");
var courFind = cour.Where(g => g.Contains("Course"));
string[] splited2;
foreach (var item in courFind)
{
splited2 = item.Split(new string[] { "Course:" }, StringSplitOptions.None);
cbListCourses.Items.Add(splited2[1]);//here is where the issues starts
}
At least you should check the array length:
var cour = File.ReadAllLines(#"CourseFile.txt");
var courFind = cour.Where(g => g.Contains("Course"));
string[] splited2;
foreach (var item in courFind)
{
splited2 = item.Split(new string[] { "Course:" }, StringSplitOptions.None);
if(splited2.Length >= 2)
cbListCourses.Items.Add(splited2[1]);//here is where the issues starts
}
foreach (var item in courFind)
{
splited2 = item.Split(new string[] { "Course:" }, StringSplitOptions.None);
cbListCourses.Items.Add(splited2[0]); // Array Index starts with 0
}
You have to check array's Length (what if the file has, say, empty lines?)
var data = File
.ReadLines(#"CourseFile.txt")
.Select(line => new string[] { "Course:" }, StringSplitOptions.None)
.Where(items => item.Length >= 2) // Check array's Length
.Select(items => items[1]) // Now it's safe to address the 2nd item
.ToArray(); // ComboBox.Items.AddRange wants array
cbListCourses.Items.AddRange(data);
The file might not contain any colon character (:) in the line.
You should check as if
var courFind = cour.Where(g => g.Contains("Course:"));
instead of the previous.
Test cases followed:
File with Empty content
with content -> Course:
with content -> Course (Error same as you faced)
Code:
var cbListStudents = new List<String> ();
var cbListCourses = new List<String> ();
var d = File.ReadAllLines (#"res/TestFile.txt");
var t = d.Where (g => g.Contains ("Student Name"));
string[] splited;
foreach (var item in t) {
splited = item.Split (new string[] { "Student Name:" }, StringSplitOptions.None);
cbListStudents.Add (splited[1]);
}
var cour = File.ReadAllLines (#"res/TestFile2.txt");
var courFind = cour.Where (g => g.Contains ("Course"));
string[] splited2;
foreach (var item in courFind) {
splited2 = item.Split (new string[] { "Course:" }, StringSplitOptions.None);
cbListCourses.Add (splited2[1]); //here is where the issues starts
}
i have the following simplified input:
List<string[]> sList = new List<string[]>();
sList.Add(new string[]{ "Product", "ProductID"});
sList.Add(new string[] { "Fork", "1" });
sList.Add(new string[] { "Spoon", "2" });
sList.Add(new string[] { "Knife", "3" });
and i want the following output
ResultList[0] == { "Product" ,"Fork","Spoon","Knife"};
ResultList[1] == { "ProductID" ,"1","2","3"};
first i solved this with 2 loops, then i changed my approach to this linq (both working):
List<string[]> Result = sList.SelectMany(x => x)
.Select((x, index) => new KeyValuePair<int, string>(index % sList.First().Length, x))
.GroupBy(s => s.Key).Select(g => g.Select(x => x.Value)
.ToArray()).ToList();
it works but it seems to be a bit circuitous to me. isn't there a simpler approach (built in?) i've overlooked?
There's a better solution for this: use Dictionary<TKey, TValue>.
Dictionary<string, string> products = new Dictionary<string, string>()
{
{ "Fork", "1" },
{ "Spoon", "2" }
};
Now you can access products.Keys (which will get product names) and products.Values (which will get product identifiers):
ResultList[0] = new [] { "Product" }.Union(products.Values);
ResultList[1] = new [] { "ProductID" }.Union(products.Keys);
Possible automatization:
List<List<string>> resultList = new [] { "Product", "ProductID" }
.Select((g, index) => (index == 0 ? new [] { g }.Union(products.Keys) : new [] { g }.Union(products.Values)).ToList())
.ToList();
BTW, as long as you've stored your data in a dictionary, I don't find a reason to add "column names" to the result if you know that product names are products.Keys and identifiers products.Values. That is, you can work with the dictionary directly...
So if you know for sure that each array contains exactly 2 elements, problem becomes a combination of two trivial selecs. However if you only know that all arrays are of the same size, but do not want to rely on exact numbers, this approach should do it:
List<string[]> Result =
sList.First()
.Select((x, i) => i)
.Select(i => sList.Select(x => x[i]).ToArray())
.ToList();
We are basically using first item in sList to figure out how many members there are in the array, and then doing a select for each of them.
What about something like this:
List<string[]> sList = new List<string[]>();
sList.Add(new string[] { "Product", "ProductID" });
sList.Add(new string[] { "Fork", "1" });
sList.Add(new string[] { "Spoon", "2" });
sList.Add(new string[] { "Knife", "3" });
var res = new List<string[]> { sList.Select(a => a[0]).ToArray(), sList.Select(b => b[1]).ToArray() };
I have this list
// sample data
List<string[]> ListOfArrays = new List<string[]> { new string[]{ "key1", "key2", "key3" },
new string[]{ "value1", "value2", "value3" },
new string[]{ "item1", "item2", "item3" , "item4"} };
i want to join all paralleled indexes in the array with comma delimited string, this will result in converting List<string[]> to string[]
The expected result
// expected result
string[] joinedList = new string[] { "key1, value1, item1",
"key2, value2, item2",
"key3, value3, item3",
"item4"};
is there a simple way to do this ? something like
string[] string.JoinMultiple(string delimiter, List<string[]>);
in the above case could be used like
string[] joinedList = string.JoinMultiple(",", ListOfArrays);
i've been searching a lot to find a solution for this but with no hope.
You can try this:
string[] joinedList = listOfArrays.SelectMany(
strings => strings.Select((s, i) => new {s, i}))
.ToLookup(arg => arg.i, arg => arg.s)
.Select(grouping => string.Join(",", grouping)).ToArray();
or extension method:
string[] joinedList = listOfArrays.JoinMultiple(",");
...
public static string[] JoinMultiple(this List<string[]> lists,string delimiter)
{
return lists.SelectMany(
strings => strings.Select((s, i) => new {s, i}))
.ToLookup(arg => arg.i, arg => arg.s)
.Select(grouping => string.Join(delimiter, grouping)).ToArray();
}
I just put this method together. It produces the results you described.
public static string[] JoinMultiple(string delimiter, List<string[]> lists)
{
int maxListLength = lists.Max(l => l.Count());
string[] result = new string[maxListLength];
for (int i = 0; i < maxListLength; i++)
{
result[i] = String.Join(delimiter,
lists.Select(l => (i < l.Count()) ? l[i] : null)
.Where(s => s != null));
}
return result;
}
The file in question can have one or more blocks, each block starts with Processname:;ABC Buying.
What is the best way using Linq to split the file contents into blocks based on the occurrence of the line "Processname:; ABC Buying".
This doesn't seem to work correctly...
var lines = File.ReadAllLines(path).OfType<string>().ToList();
var grps = lines.GroupBy(blocks => blocks.Contains("Processname:;ABC Buying"));
File
Processname:;ABC Buying
ID:;31
Message Date:;08-02-2012
Receiver (code):;12345
Object code:
Location (code):;12345
Date;time
2012.02.08;00:00;0;0,00
2012.02.08;00:15;0;0,00
2012.02.08;00:30;0;0,00
2012.02.08;00:45;0;0,00
2012.02.08;01:00;0;0,00
2012.02.08;01:15;0;0,00
Processname:;ABC Buying
ID:;41
Message Date:;08-02-2012
Receiver (code):;12345
Object code:
Location (code):;12345
Date;time
2012.02.08;00:00;0;17,00
2012.02.08;00:15;0;1,00
2012.02.08;00:30;0;15,00
2012.02.08;00:45;0;0,00
2012.02.08;01:00;0;0,00
2012.02.08;01:15;0;9,00
Simple and easy:
var lines = File.ReadLines(path);
List<List<string>> groups = new List<List<string>>();
List<string> current = null;
foreach(var line in lines){
if (line.Contains("Processname:;ABC Buying")){
current = new List<string>();
groups.Add(current);
}
else if (current != null) {
current.Add(line);
}
}
So .. you should really do something like what Ahmed showed.
That being said though, you could do it using only Linq doing something like this (not very efficient) code:
var lines = new[] { "wierd", "a1", "b1", "b2", "b3", "a2", "b4", "a3", "b5", "b6" };
List<List<string>> groups = lines
.Select((x, i) => Tuple.Create(x, x.StartsWith("a") ? new int?(i) : null))
.Aggregate(Tuple.Create<IEnumerable<Tuple<string, int>>, Nullable<int>>(Enumerable.Empty<Tuple<string, int>>(), null),
(acc, x) => x.Item2.HasValue
? Tuple.Create(acc.Item1.Concat(new[] { Tuple.Create(x.Item1, x.Item2 ?? -1) }), x.Item2)
: Tuple.Create(acc.Item1.Concat(new[] { Tuple.Create(x.Item1, acc.Item2 ?? -1) }), acc.Item2))
.Item1
.GroupBy(x => x.Item2)
.Select(x => x.Select(y => y.Item1).ToList())
.ToList();
foreach(var group in groups)
{
Console.WriteLine("--New group--");
foreach (var line in group)
{
Console.WriteLine(line);
}
}
Test it here: https://compilify.net/2tr
I have a jagged array that looks like this:
string[][] list = new string[d.Rows.Count + 1][];
int c = 0;
while (c < d.Rows.Count)
{
list[c] = new string[]
{
d.Rows[c].ItemArray[2].ToString(),
d.Rows[c].ItemArray[1].ToString(),
d.Rows[c].ItemArray[4].ToString(),
d.Rows[c].ItemArray[5].ToString(),
d.Rows[c].ItemArray[7].ToString(),
d.Rows[c].ItemArray[3].ToString(),
d.Rows[c].ItemArray[14].ToString()
};
c += 1;
}
return list;
Now, for a new requirement, i need only the items from this array whose value at this location: list[x][0] are equal to any of the following strings: "Text", "FullText", "FullMatch"
I got started with a regular array i could do this: but it obvioulsy won't work for a jagged array.
string[][] newlist = list.where(item => item.equals("Text");
Does any one know how to extend this for my situation?
You can do a where on list which will iterate over each one-dimensional array, then compare element 0 to the strings given.
string[][] newlist = list
.Where(item => item[0].Equals("Text")
|| item[0].Equals("FullText")
|| item[0].Equals("FullMatch"))
.ToArray();
Tested this on some sample data as shown below:
var list = new string[][]
{
new string[] { "Text", "A", "B", "C", "D" },
new string[] { "None", "Z", "C" },
new string[] { "FullText", "1", "2", "3" },
new string[] { "FullMatch", "0", "A", "C", "Z" },
new string[] { "Ooops", "Nothing", "Here" },
};
string[][] newlist = list.Where(item => item[0].Equals("Text")
|| item[0].Equals("FullText")
|| item[0].Equals("FullMatch")).ToArray();
// now display all data...
foreach (string[] row in newlist)
{
Console.Write("Row: ");
foreach (string item in row)
{
Console.Write(item + " ");
}
Console.WriteLine();
}
This worked correctly with output being:
Row: Text A B C D
Row: FullText 1 2 3
Row: FullMatch 0 A C Z
If you wanted a fully LINQ-based solution, then I think the following should do the trick (although I haven't tested it, because I'm not usre what the variable d refers to):
var res =
(from c in Enumerable.Range(0, d.Rows.Count)
let list = new string[] {
d.Rows[c].ItemArray[2].ToString(),
d.Rows[c].ItemArray[1].ToString(),
d.Rows[c].ItemArray[4].ToString(),
d.Rows[c].ItemArray[5].ToString(),
d.Rows[c].ItemArray[7].ToString(),
d.Rows[c].ItemArray[3].ToString(),
d.Rows[c].ItemArray[14].ToString()
}
where list[0] == "Text" || list[0] == "FullText" || list[0] == "FullMatch"
select list).ToArray();
A jagged array is just an array of arrays, so you can process it using LINQ. The only trick is that individual items will be arrays (representing your columns). After using Where, you can turn the result back to an array using ToArray:
string[][] newlist = list.Where(item => item[0] == "Text" || ... ).ToArray();