Add string[] to List<string[]> c# .Net - c#

I have a List<string[]> parsedData.
I have another List<string[]> newData = {n, 1, 2, 3}.
The strings in parsedData seems to be stored the following way:
1. a b c
2. 1 2 3
3. 1 2 3
Which is perfect. When I add the newData to parsedData it becomes like this:
1. a b c
2. 1 2 3
3. 1 2 3
4. n 1 2 3
I'm searching for a way to transpose the newData list and add it in a similar fashion but I have problem getting it to work. Can someone please shed some light on this matter.
This is the code when I parse parsedData:
List<string[]> parsedData = new List<string[]>();
string[] fields;
try
{
TextFieldParser parser = new TextFieldParser(path);
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
fields = parser.ReadFields();
for (int i = 0; i < fields.Length; i++)
{
if (fields[i] == "NaN")
fields[i] = null;
}
parsedData.Add(fields);
}
parser.Close();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
return parsedData;
The string[] newData is added to a list in this way:
List<string[]> newData = new List<string[]>();
string[] mCol = mean.meanCol(c, parsedData); // function that returns a string[]
newData.Add(mCol);
I then want to join the two lists as explained above so it looks like this:
a b c n
1 2 3 1
1 2 3 2
1 2 3 3

Okay, I assume you have a text file that looks something like this:
a,b,c
1,2,3
1,2,3
and after you read/parse it, you end up with something that is equal to:
List<string[]> parsedData =
new List<string[]>
{
new []{"a", "b", "c"},
new []{"1", "2", "3"},
new []{"1", "2", "3"}
};
and then you want to be able to add something like:
List<string[]> newData =
new List<string[]>
{
new []{"n", "1", "2", "3"},
new []{"m", "1", "2", "3"},
// ... added more new data
};
Well, first the newData is "turned" 90° and each item has one more data point than the existing ones (a has 1 and 1, while n has 1, 2 and 3).
So even in the ideal way, you'd end up with:
List<string[]> combinedData =
new List<string[]>
{
new []{"a", "b", "c", "n", "m"},
new []{"1", "2", "3", "1", "1"},
new []{"1", "2", "3", "2", "2"},
new []{null, null, null, "3", "3"}
};
And second, you use string[] instead of List<string>, so growing is more complicated.
In any case, a List<string[]> is a suboptimal data structure to express what you want: having several channel names and being able to store several measurements per channel name.
I'd suggest a Dictionary<string, List<string>>, where the key is the channel name and the value, which is a List<string> contains the list of measurements for that channel name.
There are probably easier ways to do it, but one way to get from your List<string[]> to a Dictionary<string, List<string>> could work like:
var transposedData = new Dictionary<string, List<string>>();
var minArrayLength = parsedData.Min(a => a.Length);
for (var index = 0; index < minArrayLength; index++)
{
string key = null;
foreach (var array in parsedData)
{
if (key == null)
{
key = array[index];
transposedData[key] = new List<string>();
}
else
{
transposedData[key].Add(array[index]);
}
}
}
Btw. don't get confused by the var keyword, I like to use that a lot.
Once you have it in this data structure, you can add your newData like this:
foreach (var array in newData)
{
var key = array[0];
transposedData[key] = new List<string>();
// skip the key
for (var index = 1; index < array.Length; index++)
{
transposedData[key].Add(array[index]);
}
}
Now, the question is, are you happy with that, or do you really need a List<string[]> again? If so, you need to transpose it back. Maybe with something like this:
// using a list inside for now for easier adding
var backTranspose = new List<List<string>>();
// determine the max number of measurements for a channel name
var maxLength = transposedData.Values.Max(l => l.Count);
// use one more to include key
for (var valueIndex = 0; valueIndex <= maxLength; valueIndex++)
{
backTranspose.Add(new List<string>());
}
foreach (var kvp in transposedData)
{
var index = 0;
backTranspose[index].Add(kvp.Key);
for (var valueIndex = 0; valueIndex < maxLength; valueIndex++)
{
index++;
if (kvp.Value.Count > valueIndex)
{
backTranspose[index].Add(kvp.Value[valueIndex]);
}
else
{
backTranspose[index].Add(null);
}
}
}
// turn the lists back into arrays
parsedData = new List<string[]>();
foreach (var list in backTranspose)
{
parsedData.Add(list.ToArray());
}
One caveat though, a Dictionary is not ordered. So you might end up with channel names at different positions. But of course, the channel name and the corresponding measurement data will have the same index.

You can use LINQ and Concat to achieve what you need to do.
var allData = parsedData.Concat( newData ).ToList();

Related

find item in list , and get other item from other list at same location using linq

I have a class where it has a collection of list. I want to search a parameter inside one of the list. So the location that I found the list, I want to get at the same location at other list in the same class...
How to achieve this?
void Main()
{
var myListCollectionObj = new myListCollection();
Console.WriteLine(myListCollectionObj.firstList);
Console.WriteLine(myListCollectionObj.secondList);
Console.WriteLine(myListCollectionObj.thirdList);
var testFirstList = myListCollectionObj.firstList.Where(x => x == 3); //then i want to get "33", and 333 from secondList and thirdList respectively
Console.WriteLine(testFirstList);
}
class myListCollection
{
public List<int> firstList = new List<int>(){ 1, 2, 3, 4, 5};
public List<string> secondList = new List<string>(){ "11", "22", "33", "44", "55"};
public List<int> thirdList = new List<int>(){ 111, 222, 333, 444, 555};
}
int index = myListCollectionObj.firstList.IndexOf(3);
string elem2;
int elem3;
if (index >= 0 && index < myListCollectionObj.secondList.Length)
elem2 = myListCollectionObj.secondList[index]
if (index >= 0 && index < myListCollectionObj.thirdList.Length)
elem3 = myListCollectionObj.thirdList[index]
You don't need LINQ for that, only List<T>'s own IndexOf() method and indexer property:
int index = myListCollectionObj.firstList.IndexOf(3);
string secondValue = myListCollectionObj.secondList[index];
int thirdValue = myListCollectionObj.thirdList[index];
You may want to add error handling: if 3 is not contained in firstList, an index of -1 is returned by IndexOf().
I guess the best way if there are more than one 3 values would be using simple for loop:
var testFirstList = new List<int>();
var testSecondList = new List<string>();
var testThirdList = new List<int>();
for (var i = 0; i < myListCollectionObj.firstList.Length; ++i) {
if (myListCollectionObj.firstList[i] == 3) {
testFirstList.Add(myListCollectionObj.firstList[i]);
testSecondList.Add(myListCollectionObj.secondList[i]);
testThirdList.Add(myListCollectionObj.thirdList[i]);
}
}
A good guideline is that if you find yourself combining indices and LINQ, you probably have other options available. In this case, a good alternative would be using Zip
This approach lets you combine the 3 collections and act upon the resulting zipped collection as a single entity such that indices are no longer directly required.
var result = firstList.Zip(
secondList.Zip(thirdList,
(b, c) => new { b, c }),
(a, b) => new { Value1 = a, Value2 = b.b, Value3 = b.c })
.Where(x => x.Value1 == 3).ToList();
result.ForEach(v => Console.WriteLine(v));
correct me if i am wrong are you looking for the index of the item you searched in first list and then use the same index to retrieve from other list
If yes
Try this
var testFirstList = myListCollectionObj.firstList.Where(x => x == 3).FirstOrDefault(); //then i want to get "33", and 333 from secondList and thirdList respectively
var index = myListCollectionObj.firstList.IndexOf(testFirstList);

Alter a list of strings based on the items

I have a problem with a list that I want to alter, before outputting it back to the client.
For the sake of the question I will post an example of the list and how I need to result to look, because I have looked at Intersect, Except and everything else I could think of, but didn't get the result I am looking for.
Example List:
1, 4, 6, 8
1, 2, 6, 8
2, 4, 6, 8
3, 4, 5, 7
Required Result:
1, 4, 6, 8 //Initial row
-, 2, -, - //Items that have not changed will show as a -
2, 4, -, -
3, -, 5, 7
I really hope I explained it well.
I would be happy to explain this further if needed.
Thanks in advance for the advice, so far I have wrecked my brain over this. ;)
What I tried is too much to type here, so here is what I have so far. Except simply won't do anything with the data because it thinks the rows are different, so they just stay the same.
private List<List<string>> FilterData(List<string[]> datatable)
{
List<string> previousRow = new List<string>();
List<string> currentRow = new List<string>();
List<string> rowDifferences = new List<string>();
List<List<string>> resultingDataset = new List<List<string>>();
foreach (var item in datatable)
{
if (previousRow == null)
{
previousRow = item.ToList();
continue;
}
currentRow = item.ToList();
rowDifferences = currentRow.Except(previousRow).ToList();
resultingDataset.Add(rowDifferences);
}
return resultingDataset;
}
Few things you have to change in your code;
Here is code:
private List<string[]> FilterData(List<string[]> datatable)
{
// List is made of String Array, so need string[] variable not list
string[] previousRow = null ;
string[] currentRow;
string[] rowDifferences ;
// to store the result
List<string[]> resultingDataset = new List<string[]>();
foreach (var item in datatable)
{
if (previousRow == null)
{
previousRow = item;
resultingDataset.Add(previousRow); // add first item to list
continue;
}
currentRow = item;
// check and replace with "-" if elment exist in previous
rowDifferences = currentRow.Select((x, i) => currentRow[i] == previousRow[i] ? "-" : currentRow[i]).ToArray();
resultingDataset.Add(rowDifferences);
// make current as previos
previousRow = item;
}
return resultingDataset;
}
check this dotnetfiddle
private static List<List<string>> FilterData(List<List<string>> datatable)
{
var result = new List<List<string>>();
for(var rowindex = 0; rowindex < datatable.Count; rowindex++)
{
// Clone the string list
var refrow = datatable[rowindex]
.Select(item => (string)item.Clone()).ToList();
result.Add(refrow);
// First row will not get modify anyway
if (rowindex == 0) continue;
var row = result[rowindex];
// previous row of result has changed to "-", so use the original row to compare
var prevrow = datatable[rowindex - 1];
for(var columnindex = 0; columnindex < row.Count; columnindex++)
{
if (row[columnindex] == prevrow[columnindex])
row[columnindex] = "-";
}
}
return result;
}
fiddle
public static List<List<T>> RemoveDuplicates<T>(this List<List<T>> items, T replacedValue) where T: class
{
List<List<T>> ret = items;
items.ForEach(m=> {
var ind = items.IndexOf(m);
if(ind==0)
{
ret.Add(items.FirstOrDefault());
}
else
{
var prevItem = items.Skip(items.IndexOf(m)-1).FirstOrDefault();
var item = new List<T>();
for(var a = 0; a < prevItem.Count; a++)
{
item.Add(prevItem[a] == m[a]? replacedValue : m[a]);
}
ret.Add(item);
}
});
return ret;
}
How to use it:
var items = new List<List<string>>{
new List<string>{ "1", "4", "6", "8" },
new List<string>{ "1", "2", "6", "8" },
new List<string>{ "2", "4", "6", "8" },
new List<string>{ "3", "4", "5", "7" }
};
var result = items.RemoveDuplicates("-");
dotNetFiddle: https://dotnetfiddle.net/n36p64

How to calculate sum of items in a List<List<string>> using C#

I have a List<List<string>> .Each item in these List, will be used to display as columns of a report.
I need to calculate the sum (after converting the string to decimal),of each each column in the report and show it as the footer.
It is not sure, that the number of items in each list will match with other lists.
That means, each item of the lists should be converted and sum up and add in to another list, which will be used to display as the footer row.
Please suggest me, which method would be better to use to achieve this in performance wise.
List<List<string>> test = new List<List<string>>();
List<string> columnTotal = new List<string>();
decimal total = 0;
foreach (var item in test)
{
//Get first element
// decimal dec = Convert.ToDecimal(first element);
// total =total +dec ; ....
//Once first element in all the list has been added,
//columnTotal .add(convert.tostring(total));
//Now get the second element ...so on
}
this is my actual requirement.
Give this a try. I assume you're using a 2D table and not a jagged table.
// Assuming a 2D table and not jagged
List<List<string>> table = new List<List<string>>
{
new List<string> { "1", "2", "3" },
new List<string> { "1", "2", "3" },
new List<string> { "1", "2", "3" },
new List<string> { "2", "3", "4" }
};
List<decimal> footerTotals = new List<decimal>();
for (int i = 0; i < table[0].Count; i++)
{
// Sum the columns
footerTotals.Add(table.Sum(t => decimal.Parse(t[i])));
}
table.ForEach(row => Console.WriteLine(String.Join("\t", row)));
Console.WriteLine(String.Join("\t", footerTotals));
Results:
1 2 3
1 2 3
1 2 3
2 3 4
5 9 13

remove duplicate based on position

I have two lists like below in C#.
List 1 = [{Item="A",position =1},{Item="B",position =2},{Item="A",position =3}]
List 2 = [{Item="AA",position =1},{Item="BB",position =2},{Item="AC",position =3}]
Now i want to remove duplicate values in the List 1 and that position should be removed in the List 2.
Example o/p
List 1 = [{Item="A",position =1},{Item="B",position =2}]
List 2 = [{Item="AA",position =1},{Item="BB",position =2}]
Can any one help me. Thanks.
List<string> lst1 = new List<string> { "A", "B", "A" };
List<string> lst2 = new List<string> { "AA", "BB", "AC" };
HashSet<string> seen = new HashSet<string>();
for (int i = 0; i < lst1.Count; i++) {
if (!seen.Add(lst1[i])) {
lst1.RemoveAt(i);
lst2.RemoveAt(i);
i--;
}
}
I used a HashSet to "save" the "already seen" elements of lst1 and then simply cycle the lst1 and remove the duplicate elements. HashSet.Add returns true if the HashSet doesn't already have an element, false if it already has it.
It isn't exactly clear what you want/what you have, but here there is the solution for another possible use case:
public class MyObject {
public string Item;
public int Position;
}
List<MyObject> lst1 = new List<MyObject> {
new MyObject { Item = "A", Position = 1 },
new MyObject { Item = "B", Position = 2 },
new MyObject { Item = "A", Position = 3 },
};
List<MyObject> lst2 = new List<MyObject> {
new MyObject { Item = "AA", Position = 1 },
new MyObject { Item = "BB", Position = 2 },
new MyObject { Item = "AC", Position = 3 },
};
HashSet<string> seen = new HashSet<string>();
HashSet<int> toBeDeleted = new HashSet<int>();
for (int i = 0; i < lst1.Count; i++) {
if (!seen.Add(lst1[i].Item)) {
toBeDeleted.Add(lst1[i].Position);
lst1.RemoveAt(i);
i--;
}
}
if (toBeDeleted.Count > 0) {
for (int i = 0; i < lst2.Count; i++) {
if (toBeDeleted.Contains(lst2[i].Position)) {
lst2.RemoveAt(i);
i--;
}
}
// or equivalent and shorter, without the for cycle
//lst2.RemoveAll(x => toBeDeleted.Contains(x.Position));
}
In this case in a first pass on lst1 we remove the duplicate items (as seen in the first example) and "save" the Positions that need to be deleted in the HashSet<int> tobedeleted and then we do a second pass on lst2 to remove the elements that need deleting.
Much not clear what you want do, but I try with this:
var filteredList1 = list1.GroupBy(x => x.Item).Select(g => g.First()).ToList();
var removeElements = list2.Where(f => !filteredList1.Any(t => t.Position == f.Position)).ToList();
removeElements.ForEach(x => list2.Remove(x));

Query first element in all columns in a jagged array

int numPics = 3; //Populated from a query
string[] picID = new string[numPics];
//pictureTable is constructed so that it correlates to pictureTable[column][row]
string[][] pictureTable = null; //assume the table has data
for (int i = 0; i < numPics; i++)
{
//LINQ query doesn't work. Returns an IEnumerable<string> instead of a string.
picID[i] = pictureTable.Where(p => p[0].Equals("ID")).Select(p => p[i]);
}
I am new to LINQ, but I've been searching and haven't found an answer. I want to be able to check the first string of every column in my pictureTable using LINQ to see if it matches a string. Then, I want to take that column and extract data from each of the rows from 0 to i. I understand I can do it with a for loop by changing the column and keeping the row the same, but I want to use LINQ to achieve the same result.
Also if it is possible to get rid of the first for loop and achieve the same result, I would really be interested in that as well.
EDIT: Lets say we have a table that has the following data, keep in mind everything is a string.
Column Name [ID] [Name] [Age]
Row 1 [1] [Jim] [25]
Row 2 [2] [Bob] [30]
Row 3 [3] [Joe] [35]
I want to be able to query a columns name, then be able to get data from it, either by index or querying the row's data. I'll give an example using a for loop that achieves what I want.
string[][] table = new string[][] {
new string[] { "ID", "Name", "Age" },
new string[] { "1", "Jim", "25" },
new string[] { "2", "Bob", "30" },
new string[] { "3", "Joe", "35" }};
string[] resultRow = new string[table.GetLength(1)];
for (int i = 0; i < table.GetLength(0); i++)
{
if (table[i][0] == "Name") //Given this in a LINQ Query
{
Console.WriteLine("Column Name = {0}\n", table[i][0]);
for (int j = 1; j < table.GetLength(1); j++) //starts at 1
{
resultRow[i] = table[i][j]; //This is what I want to happen.
}
break; //exit outer loop
}
}
//Output:
//Column Name = Name
I think this would give you the equivalent of what you are looking for in your resultRow array
string[][] table = new string[][] {
new string[] { "ID", "Name", "Age" },
new string[] { "1", "Jim", "25" },
new string[] { "2", "Bob", "30" },
new string[] { "3", "Joe", "35" }
};
//get index of column to look at
string columnName = "Name";
var columnIndex = Array.IndexOf(table[0], columnName, 0);
// skip the title row, and then skip columns until you get to the proper index and get its value
var results = table.Skip(1).Select(row => row.Skip(columnIndex).FirstOrDefault());
foreach (var result in results)
{
Console.WriteLine(result);
}
One other thing to look at would be SelectMany, as you can use it to flatten mlutiple lists into a single list.
Do you just want to concatenate the strings together? If so you can just do this:
picID[i] = string.Concat(pictureTable.Where(p => p[0].Equals("ID")).Select(p => p[i]));

Categories