Easily Change A List After THE nth item - c#

I got a list:
var x = new List<string>(){"a","b","c"}
I am looking for an pretty easy way to change all items after a
example:
var x = new List<string>(){"a","b","c"}
var y = new List<string>(){"d","e","f"}
x.addAfterFirst(y);
result x= "a","d","e","f"
I know that' x.Skip(1)' can return me the info. I need to set it.

You can use the Take Extension Method to take the first n items from x and concat them with y using the Concat Extension Method:
List<string> x = new List<string> { "a", "b", "c" };
List<string> y = new List<string> { "d", "e", "f" };
int n = 1;
List<string> result = x.Take(n).Concat(y).ToList();
// result == { "a", "d", "e", "f" }
If you want to modify x in-place instead of creating a new list, you can use the RemoveRange Method to remove all items after the first n items, and the AddRange Method to append y to x:
List<string> x = new List<string> { "a", "b", "c" };
List<string> y = new List<string> { "d", "e", "f" };
int n = 1;
x.RemoveRange(n, x.Count - n);
x.AddRange(y);
// x == { "a", "d", "e", "f" }

Make use of InsertRange will do you task
var x = new list<string>(){"a","b","c"}
var y = new list<string>(){"d","e","f"}
x.InsertRange(2,y);
Edit
now if you want to remove element
var x = new list<string>(){"a","b","c"};
int xlength = x.Count() - 1;
var y = new list<string>(){"d","e","f"};
int ylength = y.Count() - 1;
x.InsertRange(2,y);
x.RemoveRang( 2 + ylength, xlength- 2);

Your result doesn't match the full description
Are you wishing to insert or replace
Are you needing to modify the existing collection or could you accept a new collection?
All examples use the following initialization
var insertIndex=1;
var x = new List<string>(){"a","b","c"};
var y = new List<string>(){"d","e","f"};
New Collection Replace
var result=x.Take(insertIndex).Concat(y).ToList();
New Collection Insert
var result=x.Take(insertIndex).Concat(y).Concat(x.Skip(insertIndex)).ToList();
Modify Collection Replace
x.RemoveRange(insertIndex,x.Count-insertIndex);
x.AddRange(y);
Modify Collection Insert
x.InsertRange(insertIndex,y);

Non-LINQ extention way:
int i;
for (i=0;i < y.Count;i++)
{
if (i+1 < x.Count)
x[i+1] = y[i];
else
x.Add(y[i]);
}
//If you dont want trailing elements to remain in x
for (;i < x.Count;i++)
x.RemoveAt(i);

Related

Identify common Lists in List and return Distinct Lists in List

I have a following List, I need to iterate through the list and see if the list has identical elements in it and return only a unique list. Could anyone please let me know what's wrong with the following code and a proper way to do it?
Also, Linq way to do it, if any?
Expected Solution would be = {{"a", "b", "c"},{"e", "b", "c" }}
class Program1
{
static void Main(string[] args)
{
List<string>[] stringLists = new List<string>[3]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "e", "b", "c" },
new List<string>(){ "a", "b", "c" }
};
List<List<string>> prt = new List<List<string>>();
/* I DONT UNDERSTAND WHY THIS IS NOT WORKING !!!!!!!!!!!!!!! */
foreach (var item in stringLists)
{
for (int i = 0; i < item.Count; i++)
{
if (item == stringLists[i] && (!prt.Contains(item)))
{
prt.Add(item);
}
}
}
}
}
You can try good old Distinct with a custom IEqualityComparer<T>:
using System.Linq;
...
public class SequenceComparer<T> : IEqualityComparer<IEnumerable<T>> {
public bool Equals(IEnumerable<T> x, IEnumerable<T> y) {
return Enumerable.SequenceEqual(x, y);
}
//TODO: Suboptimal HashCode implementation
public int GetHashCode(IEnumerable<T> obj) {
return obj == null
? 0
: obj.Count();
}
}
...
var stringLists = new List<string>() {
new List<string> {"a", "b", "c"},
new List<string> {"e", "b", "c"},
new List<string> {"a", "b", "c"}
};
// All you have to do is to put Distinct
var result = stringLists
.Distinct(new SequenceComparer<string>())
.ToList(); // If you want materialization
Test:
Console.Write(string.Join(Environment.NewLine, result
.Select(list => string.Join(", ", list))));
Outcome:
a, b, c
e, b, c
You need to do union on all the list and than do a distinct.
Something like this, just iterate through the list and union it with last result:
List<string> result = new List<string>();
foreach (var list in stringLists)
{
result = result.Union(list).ToList();
}
result = result.Distinct().ToList();
prt.Contains(item) expression compares List's with their reference not by their elements, so it's a bad choice for determining if two lists are duplicates.
Try following
var stringLists = new List<string>[3]
{
new List<string> {"a", "b", "c"},
new List<string> {"e", "b", "c"},
new List<string> {"a", "b", "c"}
};
var prt = new List<List<string>>();
foreach (var item in stringLists)
{
if(prt.All(it => it.Count != item.Count || it.Except(item).Any()))
prt.Add(item);
}
Dmitry Bychenko's answer is the way to go.
For your special case and with data that does not contain , you could get away with:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program1
{
public static void Main()
{
var stringLists = new List<List<string>>{
new List<string> {"a", "b", "c"},
new List<string> {"e", "b", "c"},
new List<string> {"a", "b", "c"}
};
var prt = stringLists
.Select(l => string.Join(",", l)) // make it a string separated by ,
.Distinct() // distinct it using string.Distinct()
.Select(l => l.Split(',').ToList()); // split it again at , and make it List
foreach (var p in prt)
{
foreach (var c in p)
Console.WriteLine(c);
Console.WriteLine();
}
}
}
There is lots of un-needed object creation in this approach - but i would work (until your data contains a , - then is messes your lists up).
Output:
a
b
c
e
b
c
Your misunderstanding is that you expect the statement
prt.Contains(item)
to return true when the sequence of strings in item already exists in prt. However the test internal to Contains used to determine this is a reference equality, not a item by item equality. Here is a minimal example to illustrate this:
void Main()
{
Console.Write( (new []{new []{ "a", "b", "c" }}).Contains(new[] { "a", "b", "c" }));
// Prints false
}
You either need to use a deep equals comparer like #Dmitry's answer which creates a digest (hash) of each list and compares the digests, or to do it explicitly, like this code:
class Program1
{
static void Main(string[] args)
{
List<string>[] stringLists = new List<string>[3]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "e", "b", "c" },
new List<string>(){ "a", "b", "c" }
};
List<List<string>> prt = new List<List<string>>();
for(int i = 0; i < 3; i++)
{
bool isDifferentFromAllOthers = true;
for(int j = 0; j < i; j++)
{
bool isSameAsThisItem = true;
for(int item = 0; item < 3; item++)
{
// !!! Here is the explicit item by item string comparison
if (stringLists[i][item] != stringLists[j][item])
{
isSameAsThisItem = false;
break;
}
}
if (isSameAsThisItem)
{
isDifferentFromAllOthers = false;
break;
}
}
if (isDifferentFromAllOthers)
{
prt.Add(stringLists[i]);
}
}
// /* I DONT UNDERSTAND WHY THIS IS NOT WORKING !!!!!!!!!!!!!!! */
// foreach (var item in stringLists)
// {
// for (int i = 0; i < item.Count; i++)
// {
// if (item == stringLists[i] && (!prt.Contains(item)))
// {
// prt.Add(item);
// }
// }
// }
}
}

Linq: Sorting the list based on another list

I have a list of strings List{"X", "W", "C", "A", "D", "B" } and I have another list of strings List{"A", "B", "C", "D"} that tells how the first list must be ordered. But the second list has only four items in it. I would like my first list to be ordered like this:
A, B, C, D, X, W. Actually the last two letters X and W doesn't matter how they are ordered, but they should be at the end of the list.
I tried this:
var newList = list1.OrderBy(i=>list2.IndexOf(i));
but this gives me only four items.
Your current code will give you 6 items. However, it will put X and W in the beginning since they have an index of -1 in list 2.
Here is how to fix that:
var list1 = new List<string> {"X", "W", "C", "A", "D", "B"};
var list2 = new List<string> {"A", "B", "C", "D"};
var newList = list1.OrderBy(x =>
{
var index = list2.IndexOf(x);
if (index == -1)
index = Int32.MaxValue;
return index;
})
.ToList();
One more way along with others.
List<string> list1 = new List<string>() {"X", "W", "C", "A", "D", "B" } ;
List<string> list2 = new List<string>() { "A", "B", "C", "D" } ;
var newList = list2.Intersect(list1)
.Union(list1.Except(list2));
Check Demo
This should work:
var newList = list1.OrderBy(i => {
var x = list2.IndexOf(i);
if(x == -1)
return int.MaxValue;
return x; });
Result (from LinqPad):

Sorting List based on another float List in C#

Given two lists below in C#:
List<string> X = new List<string>({ "a", "b", "c", "d", "e", "f", "g", "h", "i"});
List<float> Y = new List<float> ({ 0.991f, 1.471f, 3.819f, 0.003f, 2.291f, 2.887f, 2.887f, 0, 1.0f});
What will be the cleanest/shortest way of sorting X using float values from Y to get the following output?
"h", "d", "a", "i", "b", "e", "f", "g", "c"
The order for the elements having the same float "key" does not matter.
If each string key is unique and each list is perfectly matched, you can use them as keys in a dictionary using zip from System.Reactive.
var dic = X.Zip(Y, (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
Now, sort your newly formed dictionary by value.
var sortedDict = from entry in dic orderby entry.Value ascending select entry;
In a "one-liner" using query syntax, this becomes:
var dic = X.Zip(Y, (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
.OrderBy(x => x.Value);
Here's one way:
IEnumerable<string> sorted = X
.Select((value, index) => new { Index = index, Value = value })
.OrderBy(o => Y[o.Index])
.Select(o => o.Value);
Basically:
Use .Select to project your List (X) into a new sequence of anonymous objects that contains the string from X and its index within the list.
Order the sequence by the corresponding value in Y.
Select the Value portion of the anonymous object to create a new sequence containing just the strings from X.
Example: https://dotnetfiddle.net/ZjZvBR
The following code follows the bubble sorting technique...
for(int i = 1; i < max; i++)
{
for(int j = 0; j < max - i; j++)
{
if(Y[j] > Y[j + 1])
{
int temp = X[j];
X[j] = X[j + 1];
X[j + 1] = temp;
int temp1 = Y[j];
Y[j] = Y[j + 1];
Y[j + 1] = temp1;
}
}
}

C# Compare two Lists

Background: I have two lists that hold strings. List a and List b. At the moment I write the values of List a in an excel spreadsheet to column A, and the values of List b into Column. List b should have the same data as List a and be in sequence. This is not always the case.
Problem: When I write values of List b in excel, I want to write the value in the cell if it is in list a at the same point, if not I want to write an empty string into the cell.
Edit:
Thanks for replies and answers work very well, just realised that what I really need is :
If two lists are:
a = {"a", "b", "c", "d", "e" }
b = {"a", "d", "e" }
the result of the operation should be:
{ "a", "", "", "d", "e" }
One way is to zip your lists together and replace the "wrong" value in list b with an empty string:
var a = new [] {"a", "b", "c", "d"};
var b = new [] {"a", "Foo", "c", "Bar"};
var fixed_b = a.Zip(b, (x, y) => x == y ? x : "");
fixed_b now yields "a", "", "c" and "".
When writing your data to your excel spreadsheet, simply iterate over fixed_b instead of b
Edit:
According to your comments:
You could create a little helper method like this:
IEnumerable<T> FillBlanks<T>(IEnumerable<T> source, IEnumerable<T> collection, T blank)
{
using(var e = collection.GetEnumerator())
{
bool more = e.MoveNext();
foreach(var x in source)
if(more && x.Equals((T)e.Current))
{
yield return x;
more = e.MoveNext();
}
else
yield return blank;
}
}
var fixed_b = FillBlanks(a, b, String.Empty);
int max = aList.Count > bList.Count ? aList.Count : bList.Count;
for(int i = 0; i < max; ++i)
{
if(i < aList.Count)
Write(aList[i]);
if(i < bList.Count)
{
if(i < aList.Count)
Write(aList[i] == bList[i] ? bList[i] : "");
else
Write(bList[i]);
}
}
This assumes Write actually writes data to the spreadsheet.
Try this:
class Program
{
static void Main(string[] args)
{
List<string> listA = new List<string>() { "a", "b", "c" };
List<string> listB = new List<string>() { "a", "c", "b" };
var result = listB.Select((b, index) =>
(index == listA.IndexOf(b)) ? b : "");
}
}

divide a list with unknown number of elements

Let's say I have a list of unknown number of elements in string value, I want to divide it to n subarray or lists (n could be any int, for example n=3), what is best way to do it?
note: the number of elements in each group is not necessary to be equal
LINQ GroupBy and Select methods can help:
var list = new List<string>() { "A", "B", "C", "D", "E", "F", "G" };
int groupCount = 3;
var subLists = list.Select((s, i) => new {Str = s, Index = i}).
GroupBy(o => o.Index % groupCount, o => o.Str).
Select(coll => coll.ToList()).
ToList();
This code will result in subLists containing a list of three List<string> collections: {"A", "D", "G"}, {"B", "E"} and {"C", "F"}. In order to achieve that I based my grouping on element indices in the original list (there is an overload for Select method that lets you do that, see link above). You can use some other logic to select the key.
In my example subLists is a List<List<string>>. If you need an array, use ToArray where appropriate.
EDIT: using modulo operation for grouping may not be a good idea if you care about the way values are distributed between lists. Probably the better option is to do it this way:
var list = new List<string>() { "A", "B", "C", "D", "E", "F", "G" };
int groupCount = 3;
int maxPerGroup = (int)Math.Ceiling((double)list.Count / groupCount);
var subLists = list.Select((s, i) => new {Str = s, Index = i}).
GroupBy(o => o.Index / maxPerGroup, o => o.Str).
Select(coll => coll.ToList()).
ToList();
This will produce the following result: {"A", "B", "C"}, {"D", "E", "F"}, {"G"} which may be more sane way to distribute the values.
Bottom line is, you can achieve what you need by using GroupBy and Select methods, just provide the correct grouping logic that is suitable for your domain.
alternately, you can do it in linq like
var n = 4;
var i = 0;
var list = new List<string> { "a", "b", "c", "d", "e", "f", "g" };
var res = list.GroupBy(x => Math.Ceiling((double)++i / n)).Select(x=>x.Select(y=>y).ToList()).ToList();
Console.Write(res);
write this in Console program or Linqpad and change values of n to see the effect

Categories