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
Related
I have a List of List which can be of variable but repeated width. For example:
var test = new List<List<string>>();
test.Add(new List<string> {"1","2","3"});
test.Add(new List<string> {"1","4","12"});
test.Add(new List<string> {"1","2","9"});
test.Add(new List<string> {"1","4","5"});
test.Add(new List<string> {"6","7","8"});
But it could also be:
var test = new List<List<string>>();
test.Add(new List<string> {"1","2","3","3","3"});
test.Add(new List<string> {"1","4","12","1","7"});
test.Add(new List<string> {"1","2","9","9","4"});
test.Add(new List<string> {"1","4","5","8","5"});
test.Add(new List<string> {"6","7","8","2","7"});
It will never be:
var test = new List<List<string>>();
test.Add(new List<string> {"1"});
test.Add(new List<string> {"1","5"});
test.Add(new List<string> {"1","2","3"});
test.Add(new List<string> {"1","5"});
test.Add(new List<string> {"6","7","8"});
And I would like to have the list ordered left column to right column like:
["1","2","3"];
["1","2","9"];
["1","4","5"];
["1","4","12"];
["6","7","8"];
The following is a little test I setup to see what I could come up with (https://dotnetfiddle.net/B5ljig):
var test = new List<List<string>>();
test.Add(new List<string> {"1","2","3"});
test.Add(new List<string> {"1","4","5"});
test.Add(new List<string> {"1","2","3"});
test.Add(new List<string> {"1","4","5"});
test.Add(new List<string> {"6","7","8"});
var query = test.AsQueryable();
query = query.OrderBy(a=>a[0]);
var max = categories.Select(a=>a.Count()).Max();
for (int i = 1; i < max; i++)
{
query = query.ThenBy(a=>a[i]); // Error Here
}
var sorted = query.ToList();
Unfortunately the commented line errors with
'IQueryable>' does not contain a definition for 'ThenBy' and no accessible extension method 'ThenBy' accepting a first argument of type 'IQueryable>' could be found (are you missing a using directive or an assembly reference?)
Any ideas? Thoughts? Better ways.
If you want to Sort anything using your own rules, you can implement a custom comparer (IComparer<T>), IComparer<IList<string>> in this particular case:
public class MyListComparer : IComparer<IList<string>> {
private static int CompareItems(string left, string right) {
if (left.StartsWith("-"))
if (right.StartsWith("-"))
return -CompareItems(left.TrimStart('-'), right.TrimStart('-'));
else
return -1;
else if (right.StartsWith("-"))
return 1;
left = left.TrimStart('0');
right = right.TrimStart('0');
int result = left.Length.CompareTo(right.Length);
if (result != 0)
return result;
for (int i = 0; i < left.Length; ++i) {
result = left[i] - right[i];
if (result != 0)
return result;
}
return 0;
}
public int Compare(IList<string> x, IList<string> y) {
if (ReferenceEquals(x, y))
return 0;
else if (null == x)
return -1;
else if (null == y)
return 1;
for (int i = 0; i < Math.Min(x.Count, y.Count); ++i) {
int result = CompareItems(x[i], y[i]);
if (result != 0)
return result;
}
return x.Count.CompareTo(y.Count);
}
}
Then sort:
var test = new List<List<string>>();
test.Add(new List<string> { "1", "2", "3" });
test.Add(new List<string> { "1", "4", "12" });
test.Add(new List<string> { "1", "2", "9" });
test.Add(new List<string> { "1", "4", "5" });
test.Add(new List<string> { "6", "7", "8" });
// Time to sort with a custom comparer
test.Sort(new MyListComparer());
string report = string.Join(Environment.NewLine, test
.Select(line => string.Join(", ", line)));
Console.Write(report);
Outcome:
1, 2, 3
1, 2, 9
1, 4, 5
1, 4, 12
6, 7, 8
you can use the comparer with Linq query as well:
var sorted = test.OrderBy(new MyListComparer());
The problem is
1) the overuse of IQueryable, you don't need it,
2) the fact that i is actually captured, and when the query is executed, you have all "then by" that use the same i == 3, the last value after the end of the for loop! (Hence, an out of bounds exception at runtime)
Here is a working version (dotnetFiddle):
var query = test.OrderBy(a=>a[0]);
//var max = test.Select(a=>a.Count()).Max(); // If you say all lists have the same length, use `First(a => a.Count())` instead! And if they don't, then this will lead to an exception.
for (int i = 1; i < max; i++)
{
var j = i; // Intermediary variable so that 'global' i is not captured.
query = query.ThenBy(a=>a[j]);
};
var sorted = query.ToList();
On additional note, there are other solutions that use different approaches, already given, I think they feel more "idiomatic" for C# with the IComparer
There are two issues with your code. One is a syntax issue and one is a logic issue.
To remove the compilation error you are seeing, the query variable must be an IOrderedQueryable instead of the IQueryable that you have listed. If you combine the query variable definition and initial ordering in to one line like below, your issue should resolve.
var query = test.AsQueryable().OrderBy(a => a[0]);
You could also use the IOrderedEnumerable instead using
var query = test.OrderBy(a => a[0]);
The logic issue is that your code will not produce the result you are expecting. You are ordering the list of the list of strings by its first value before ordering each list of strings. In other words, your initial Orderby needs to be below your for loop. For simplicity I'm simplifying to this Linq expression:
var sorted = test
.Select(x => x.OrderBy(y => y).ToList())
.OrderBy(x => x[0])
.ToList();
Items is public list of MenuListItemViewModel items, in example below im creating new list with 2 elements:
Items = new List<MenuListItemViewModel>
{
new MenuListItemViewModel
{
Value = "500",
Letter = "D"
},
new MenuListItemViewModel
{
Value = "-500",
Letter = "W"
},
};
How to do exactly the same but with variable numbers of items i want to have in the list? Something like loop x times (like below, but it wont work in current state)
for (int i = 0; i < 5; i++)
{
new MenuListItemViewModel
{
Value = "500",
Letter = "D"
},
}
You can use LINQ:
Items = Enumerable.Range(0, number)
.Select(i => new MenuListItemViewModel
{
Value = "500",
Letter = "D"
}).ToList();
You can do
Items = new List<MenuListItemViewModel>();
for (int i = 0; i < 5; i++)
{
Items.Add(
new MenuListItemViewModel
{
Value = "500",
Letter = "D"
});
}
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);
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();
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));