Related
Say I have a list of strings
1,2,3,a,b,a,b,c,1,2
I have a second list of strings
a,b,c
I want to remove the second list from the first resulting in
1,2,3,a,b,1,2
What's the best way to do this with with two List<string>?
Most methods/questions/answers I see involve lists revolve around individual lines of second list being removed from the first (all a's... all b's... all c's...).
I don't want that... I only want to remove those where the a followed by b followed by c.
Edit:
Couple caveats: The Second List is generally two or three strings and CAN appear multiple times (Say, instead, the second list is 1,2. It's contained in the first list twice).
var list = new List<string>(new[] { "1", "2", "3", "a", "b", "a", "b", "c", "1", "2" });
var sublist = new List<string>(new[] { "a", "b", "c" });
var start = -1;
var index = 0;
while (index < list.Count - sublist.Count)
{
for (int i = 0; i < sublist.Count; i++)
{
if (list[i + index] == sublist[i] && i == 0)
{
start = i + index;
}
else if (list[i + index] != sublist[i])
{
start = -1;
index++;
break;
}
}
if (start != -1)
{
list.RemoveRange(start, sublist.Count);
index -= sublist.Count;
}
}
foreach (var item in list)
{
Console.Write(item + ",");
}
With hack:
var list = new List<string>(new[] { "1", "2", "3", "a", "b", "a", "b", "c", "1", "2" });
var sublist = new List<string>(new[] { "a", "b", "c" });
var a = string.Join("#", list);
var b = string.Join("#", sublist);
var result =
new List<string>(a.Replace(b, string.Empty).Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries));
foreach (var item in result)
{
Console.Write(item + ",");
}
This solution has very bad perfomance, but it can work for small lists.
With this few lines you could achieve the same. First convert it to String and then replace with 2nd string and convert it back to char array.
List<string> listA = new List<string>() { "1", "2", "3", "a", "b", "a", "b", "c", "1", "2" };
List<string> listB = new List<string>() { "a", "b", "c" };
string strA = string.Join("", listA);
string strB = string.Join("", listB);
strA = strA.Replace(strB, string.Empty);
List<string> resultList = strA.ToCharArray().Select(c => c.ToString()).ToList();
Below code if you need to support full fledged strings
List<string> listA = new List<string>() { "abc1", "2abc2", "3", "a", "b", "a", "b", "c", "1", "2" };
List<string> listB = new List<string>() { "a", "b", "c" };
string strA = string.Join(",", listA);
string strB = string.Join(",", listB) ;
strA = strA.Replace(strB, string.Empty).Replace(",,", ",");
List<string> resultList = strA.Split(',').ToList();
Removes multiple matches if this is what you expect. I'm not thrilled with the implementation, but it appears to work. I used a Stack (last in first out) because I'm lazy.
List<string> target = new List<string> { "1", "2", "3", "a", "b", "a", "b", "c", "1", "2", "a", "b", "c", "1" };
List<string> match = new List<string> { "a", "b", "c" };
Stack<int> matchIndexes = new Stack<int>();
for (int x = 0; x < target.Count - match.Count; x++)
{
int matches = 0;
for (int y = 0; y < match.Count; y++)
{
if (target[x + y] != match[y])
{
break;
}
else
{
matches++;
}
}
if (matches == match.Count)
{
matchIndexes.Push(x);
}
}
while(matchIndexes.Count > 0)
{
int index = matchIndexes.Pop();
target.RemoveRange(index, match.Count);
}
Just iterate through each item in list 2, and remove it from list 1.
foreach(string listItem in list2)
{
list1.Remove(listItem);
}
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):
I have list of list and want to remove duplicate from list.
Data is stored in list format say IEnumerable<IEnumerable<string>> tableData
if we consider it as table value,
parent list is for rows and child list is values of every column.
Now I want to delete all duplicate rows. from below table value A is duplicate.
List<List<string>> ls = new List<List<string>>();
ls.Add(new List<string>() { "1", "A" });
ls.Add(new List<string>() { "2", "B" });
ls.Add(new List<string>() { "3", "C" });
ls.Add(new List<string>() { "4", "A" });
ls.Add(new List<string>() { "5", "A" });
ls.Add(new List<string>() { "6", "D" });
IEnumerable<IEnumerable<string>> tableData = ls;
var abc = tableData.SelectMany(p => p).Distinct(); ///not work
after operation, I want abc should be exactly tableData format
ls.Add(new List<string>() { "1", "A" });
ls.Add(new List<string>() { "2", "B" });
ls.Add(new List<string>() { "3", "C" });
ls.Add(new List<string>() { "6", "D" });
tableData.GroupBy(q => q.Skip(1).First()).Select(q => q.First())
You can use the overload of Distinct passing in an IEqualityComparer assuming you actually have an IEnumerable<IEnumerable<PropertyData>>.
For example:
var items = tableData.SelectMany(x => x).Distinct(new TableComparer());
And the comparer:
public class TableComparer : IEqualityComparer<PropertyData>
{
public bool Equals(PropertyData x, PropertyData y)
{
return x.id == y.id;
}
public int GetHashCode(PropertyData pData)
{
return pData.id.GetHashCode();
}
}
If it's just an IEnumerable<IEnumerable<string>>, you can use Distinct() without the overload:
var items = tableData.SelectMany(x => x).Distinct();
Though your question lacks clarity..
var distinctValues = tableData.SelectMany(x => x).Distinct();
This will flatten your list of lists and select the distinct set of strings.
you can use below menioned code
List<List<string>> ls=new List<List<string>>();
ls.Add(new List<string>(){"Hello"});
ls.Add(new List<string>(){"Hello"});
ls.Add(new List<string>() { "He" });
IEnumerable<IEnumerable<string>> tableData = ls;
var abc = tableData.SelectMany(p => p).Distinct();
O/P is
Hello
He
I have a List of List of Strings, and I need to use the AddRange() Function to add set of items to it, but never duplicate items.
I used the following code :
List<List<string>> eList = new List<List<string>>();
List<List<string>> mergedList = new List<List<string>>();
//
// some code here
//
mergedList.AddRange(eList.Where(x => !mergedList.Contains(x)).ToList());
However it does not work.
All Duplicated items are added, so how could I solve that?
A)
If what you mean from duplicate is both lists contain the same elements int the same order, then
List<List<string>> eList = new List<List<string>>();
eList.Add(new List<string>() { "a", "b" });
eList.Add(new List<string>() { "a", "c" });
eList.Add(new List<string>() { "a", "b" });
var mergedList = eList.Distinct(new ListComparer()).ToList();
public class ListComparer : IEqualityComparer<List<string>>
{
public bool Equals(List<string> x, List<string> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(List<string> obj)
{
return obj.Take(5).Aggregate(23,(sum,s)=> sum ^= s.GetHashCode());
}
}
B)
If the order of elements in the list is not important, then
List<List<string>> eList = new List<List<string>>();
eList.Add(new List<string>() { "a", "b" }); <--
eList.Add(new List<string>() { "a", "c" });
eList.Add(new List<string>() { "b", "a" }); <--
var mergedList = eList.Select(x => new HashSet<string>(x))
.Distinct(HashSet<string>.CreateSetComparer()).ToList();
Try following LINQ query
mergeList.AddRange( eList.Where (x => mergeList.Where ( y => y.Intersect(x)).Any()));
I have:-
IEnumerable<IEnumerable<T>> items;
and I'd like to create:-
IEnumerable<IEnumerable<T>> results;
where the first item in "results" is an IEnumerable of the first item of each of the IEnumerables of "items", the second item in "results" is an IEnumerable of the second item of each of "items", etc.
The IEnumerables aren't necessarily the same lengths. If some of the IEnumerables in items don't have an element at a particular index, then I'd expect the matching IEnumerable in results to have fewer items in it.
For example:-
items = { "1", "2", "3", "4" } , { "a", "b", "c" };
results = { "1", "a" } , { "2", "b" }, { "3", "c" }, { "4" };
Edit: Another example (requested in comments):-
items = { "1", "2", "3", "4" } , { "a", "b", "c" }, { "p", "q", "r", "s", "t" };
results = { "1", "a", "p" } , { "2", "b", "q" }, { "3", "c", "r" }, { "4", "s" }, { "t" };
I don't know in advance how many sequences there are, nor how many elements are in each sequence. I might have 1,000 sequences with 1,000,000 elements in each, and I might only need the first ~10, so I'd like to use the (lazy) enumeration of the source sequences if I can. In particular I don't want to create a new data structure if I can help it.
Is there a built-in method (similar to IEnumerable.Zip) that can do this?
Is there another way?
Now lightly tested and with working disposal.
public static class Extensions
{
public static IEnumerable<IEnumerable<T>> JaggedPivot<T>(
this IEnumerable<IEnumerable<T>> source)
{
List<IEnumerator<T>> originalEnumerators = source
.Select(x => x.GetEnumerator())
.ToList();
try
{
List<IEnumerator<T>> enumerators = originalEnumerators
.Where(x => x.MoveNext()).ToList();
while (enumerators.Any())
{
List<T> result = enumerators.Select(x => x.Current).ToList();
yield return result;
enumerators = enumerators.Where(x => x.MoveNext()).ToList();
}
}
finally
{
originalEnumerators.ForEach(x => x.Dispose());
}
}
}
public class TestExtensions
{
public void Test1()
{
IEnumerable<IEnumerable<int>> myInts = new List<IEnumerable<int>>()
{
Enumerable.Range(1, 20).ToList(),
Enumerable.Range(21, 5).ToList(),
Enumerable.Range(26, 15).ToList()
};
foreach(IEnumerable<int> x in myInts.JaggedPivot().Take(10))
{
foreach(int i in x)
{
Console.Write("{0} ", i);
}
Console.WriteLine();
}
}
}
It's reasonably straightforward to do if you can guarantee how the results are going to be used. However, if the results might be used in an arbitrary order, you may need to buffer everything. Consider this:
var results = MethodToBeImplemented(sequences);
var iterator = results.GetEnumerator();
iterator.MoveNext();
var first = iterator.Current;
iterator.MoveNext();
var second = iterator.Current;
foreach (var x in second)
{
// Do something
}
foreach (var x in first)
{
// Do something
}
In order to get at the items in "second" you'll have to iterate over all of the subsequences, past the first items. If you then want it to be valid to iterate over the items in first you either need to remember the items or be prepared to re-evaluate the subsequences.
Likewise you'll either need to buffer the subsequences as IEnumerable<T> values or reread the whole lot each time.
Basically it's a whole can of worms which is difficult to do elegantly in a way which will work pleasantly for all situations :( If you have a specific situation in mind with appropriate constraints, we may be able to help more.
Based on David B's answer, this code should perform better:
public static IEnumerable<IEnumerable<T>> JaggedPivot<T>(
this IEnumerable<IEnumerable<T>> source)
{
var originalEnumerators = source.Select(x => x.GetEnumerator()).ToList();
try
{
var enumerators =
new List<IEnumerator<T>>(originalEnumerators.Where(x => x.MoveNext()));
while (enumerators.Any())
{
yield return enumerators.Select(x => x.Current).ToList();
enumerators.RemoveAll(x => !x.MoveNext());
}
}
finally
{
originalEnumerators.ForEach(x => x.Dispose());
}
}
The difference is that the enumerators variable isn't re-created all the time.
Here's one that is a bit shorter, but no doubt less efficient:
Enumerable.Range(0,items.Select(x => x.Count()).Max())
.Select(x => items.SelectMany(y => y.Skip(x).Take(1)));
What about this?
List<string[]> items = new List<string[]>()
{
new string[] { "a", "b", "c" },
new string[] { "1", "2", "3" },
new string[] { "x", "y" },
new string[] { "y", "z", "w" }
};
var x = from i in Enumerable.Range(0, items.Max(a => a.Length))
select from z in items
where z.Length > i
select z[i];
You could compose existing operators like this,
IEnumerable<IEnumerable<int>> myInts = new List<IEnumerable<int>>()
{
Enumerable.Range(1, 20).ToList(),
Enumerable.Range(21, 5).ToList(),
Enumerable.Range(26, 15).ToList()
};
myInts.SelectMany(item => item.Select((number, index) => Tuple.Create(index, number)))
.GroupBy(item => item.Item1)
.Select(group => group.Select(tuple => tuple.Item2));