Adding only new items from one list to another - c#

I have two lists:
List<string> _list1;
List<string> _list2;
I need add all _list2 different items on _list1...
How can I do that using LINQ?
Thanks

// Add all items from list2 except those already in list1
list1.AddRange(list2.Except(list1));

You would use the IEnumerable<T>.Union method:
var _list1 = new List<string>(new[] { "one", "two", "three", "four" });
var _list2 = new List<string>(new[] { "three", "four", "five" });
_list1 = _list1.Union(_list2);
// _distinctItems now contains one, two, three, four, five
EDIT
You could also use the method the other post uses:
_list1.AddRange(_list2.Where(i => !_list1.Contains(i));
Both of these methods are going to have added overhead.
The first method uses a new List to store the Union results (and then assigns those back to _list1).
The second method is going to create an in-memory representation of Where and then add those to the original List.
Pick your poison. Union makes the code a bit clearer in my opinion (and thus worth the added overhead, at least until you can prove that it is becoming an issue).

_list1.AddRange( _list2.Where(x => !_list1.Contains(x) ) );

Related

Get get part of list after certain value

I have got a simple question I am having a list:
List<string> test = new List<string> {"one", "two", "three", "four"}
Now I want to take for example value "three" and get all elements after it, so it would be looking like:
List<string> test = new List<string> {"three", "four"}
But we do not know where list end so it can be list of many elements and we can not define end as const.
Is it possible?
It sounds like you're looking for SkipWhile from LINQ:
test = test.SkipWhile(x => x != "three").ToList();
That will skip everything until (but not including) the "three" value, then include everything else. It then converts it to a list again.
Since you assign the filtered list back to initial one, then just remove first items up to "three" one:
int count = test.IndexOf("three");
test.RemoveRange(0, count < 0 ? test.Count : count);
This implementation doesn't create additional list, but modifies existing one.
This might do the trick for you
var list2 = test.Skip(2).Take(test.Count).ToList();
or better
var list3 = test.Skip(2).ToList();
Without LINQ it could be done something like this
List<string> outtest = new List<string>();
bool drty = false;
foreach(string st in test)
{
if(st == "three") //or whatever is the input.
drty = true;
if(drty)
outtest.Add(st);
}

LINQ query returns old results when source list is re-initialized

A coworker and I were discussing a situation where an IEnumerable query was returning an old result set after the source list had been re-initialized. Somewhere in the execution of the application the list was being set to null and re-populated with new values. The query itself was never redefined, and continued to return the old results. In fact, it didn't even matter if the source list remained null; the old results were still returned.
Here are some unit tests to demonstrate what we are seeing:
[Test]
public void QueryResultsBasedOnCurrentListEvenAfterUpdate()
{
var list = new List<string> { "Two", "Three" };
var query = list.Where(x => x.Length > 3);
var result1 = query.ToList();
list.Clear();
list.AddRange(new List<string> { "Four", "Five", "One" });
//Correctly gets an updated result set
var result2 = query.ToList();
//PASS
CollectionAssert.AreEquivalent(new List<string> { "Three" }, result1);
//PASS
CollectionAssert.AreEquivalent(new List<string> { "Four", "Five" }, result2);
}
[Test]
public void QueryResultsBasedOnCurrentListEvenAfterSetToNullAndReInstantiated()
{
var list = new List<string> { "Two", "Three" };
var query = list.Where(x => x.Length > 3);
var result1 = query.ToList();
list = null;
list = new List<string> { "Four", "Five", "One" };
var result2 = query.ToList();
//PASS
CollectionAssert.AreEquivalent(new List<string> { "Three" }, result1);
//FAIL : result2 == result1. The query wasn't evaluated against the new list
CollectionAssert.AreEquivalent(new List<string> { "Four", "Five" }, result2);
}
[Test]
public void QueryExecutionThrowsExceptionWhenListIsSetToNull()
{
var list = new List<string> { "Two", "Three" };
var query = list.Where(x => x.Length > 3);
list = null;
//FAIL : The query is still evaluated against the original list
Assert.Throws<ArgumentNullException>(() => query.ToList());
}
It seems that despite Deferred Execution, these queries are still pointing to the original list. As long as the original collection the query was built against remains alive the query correctly evaluates the results. However, if the list is re-instantiated the query remains tied to the original list.
What am I missing? Please explain...
UPDATE:
I'm seeing the same behavior for a query built as an IQueryable. Does an IQueryable also hold a reference to the original list?
I can see how that would be a bit confusing. Here's the deal, succinctly:
The "receiver" of the query is treated as a value -- it never changes. If the value is a reference to a list, the value continues to be a reference to that list. The contents of the list might change, but which list does not change.
The local variables referred to by clauses of the query are treated as variables -- the latest values of those variables are always used.
Let me give you an analogy to the real world. Suppose you are in your kitchen. You have a drawer labeled "house" and a drawer labeled "name". In the drawer labeled "house" there is a piece of paper that says "1600 Pennsylvania Avenue". In the drawer labeled "name" there is a piece of paper that says "Michelle". When you say:
var house = GetHouse("1600 Pennsylvania Avenue");
var name = "Michelle";
var query = from resident in house.Residents
where resident.FirstName == name
select resident;
That is like writing the query:
"list all the residents of the White House whose first name is (look at the piece of paper in the drawer marked "name" in my kitchen)"
The values returned by that query depend on (1) who is living in the White House, and (2) what name is written on the piece of paper when the query runs.
It is not like writing the query:
"list all the residents of (look at the piece of paper in the drawer marked "house" in my kitchen) whose first name is (look at the piece of paper in the drawer marked "name" in my kitchen)"
The object against which the query is running is not a variable. The contents of the White House can change. The name that the query is asking about can change. And therefore the results of the query can change in two ways -- with time, and with the value of the name variable. But what house the query is asking about does not ever change no matter what you do to the variable that held the reference. That variable is irrelevant to the query; its value was used to build the query.
Changing where the reference 'list' points to does not change the original data. When the query expression was written the 'Where' method took it's own reference to the data and will work on that data regardless of where the 'list' variable subsequently points.
This this case, Where gets a new instance of an IEnumerable which references the data that list currently points to, when you then change what list points to, the IEnumerable does not change, it already has it's reference to the data.
It's as simple as that query (through an IEnumerable) holds a reference to the collection it was created with.
Changing another variable referencing the same List (ie setting list to null) does not change the reference query already holds.
The first test changes the underlying data in the actual list query references, which indeed changes the result.
In the second test you create a new list which leaves query still referencing the previous list. Changing list does not change query.
The third test only nulls out list which has no effect on the reference query already holds.

C# Collections for Distinct Data

I have a bunch of string data and I can loop through it one by one. What's a good collection (and how to implement it) so that I only get the distinct strings?
The client I am doing this for doesn't even use .NET 3.5 so .Distinct is out. They use .NET framework 2.0.
And I am reading the list one at a time and don't know how many records it will have until I'm done.
One way is using Distinct to make your strings unique:
List<string> a = new List<string>();
a.AddRange(new string[] { "a", "b", "a", "c", "d", "b" });
List<string> b = new List<string>();
b.AddRange(a.Distinct());
Another resource on LINQ's Distinct: http://blogs.msdn.com/b/charlie/archive/2006/11/19/linq-farm-group-and-distinct.aspx
Another way: use a HashSet as others suggested;
HashSet<string> hash = new HashSet<string>(inputStrings);
Have a look for this link, to see how to implement it in .net 2.0: https://stackoverflow.com/a/687042/284240
If you're not on 3.5, you also can do it manually:
List<string> newList = new List<string>();
foreach (string s in list)
{
if (!newList.Contains(s))
newList.Add(s);
}
// newList contains the unique values
Another solution (maybe a little faster):
Dictionary<string,bool> dic = new Dictionary<string,bool>();
foreach (string s in list)
{
dic[s] = true;
}
List<string> newList = new List<string>(dic.Keys);
// newList contains the unique values
https://stackoverflow.com/a/1205813/284240
If you're using .Net 3.5 or above, put the strings in a List<> and use the linq method Distinct().
using System.Linq;
IEnumerable<string> strs = new List<string>(new[] { "one", "two", "three", "one" });
var distinct = strs.Distinct();
In .Net 2.0 you have no choice but to do it manually.
Perhaps I'm being dense and not fully understanding the question but can't you just use a regular List and just use the .Contains method to check if each string exists in the list before adding it in the loop? You might need to keep an eye on performance if you have a lot of strings.

How to create a list of differences based on two other lists and include duplicates

I have two lists
List<string> list1 as new List<string>();
and
List<string> list2 as new List<string>();
What I want to do is create a third list that is a list of differences.
Example:
list1 contains (Test1, Test2, Test3, Test4)
list2 contains (Test1, Test3)
I would want my new list to contain (Test 2, Test4)
I tried using
newlist = list1.Except(list2).ToList();
and for the above example it works fine.
Example 2:
list 1 contains (Test1, Test1, Test2, Test2)
list 2 is empty
I want my newlist to contain everything (Test1, Test1, Test2, Test2)
If I use the same Except method I was using above I get in my newlist (Test1, Test2)
Is there a way I can include the duplicates?
One more final example so it is hopefully clear on what I am looking for
list1 contains (Test1, Test2, Test2, Test3, Test4)
list2 contains (Test1, Test2, Test5, Test6)
I would want my newlist to contain (Test2, Test3, Test4, Test5, Test6)
One more thing is that list1 and list2 are in no particular order and newlist does not need to be in any particular order either.
I think getting what you are looking for here is going to be really hard to do with an out of the box. Really what you need to do is "summarize" both lists something like a count of each item, then from there, do a different on items, THEN a difference on counts.
You'll have to write this one yourself. The way Except probably works is by putting all of the elements from the first collection into the equivalent of aHashSet<T> and removing every item that's in the second.
What I'd do is something similar: put everything from the first collection into aDictionary<T, int> with values corresponding to occurrence counts. Enumerating over the second collection, subtract from these counts. Then reconstruct a list from the updated counts after subtraction.
Does that make sense?
UPDATE: I have compressed my previous code into something a little smaller:
var list1 = new List<string>() { "Test1", "Test2", "Test2", "Test3", "Test4" };
var list2 = new List<string>() { "Test1", "Test2", "Test5", "Test6" };
var newlist = new List<string>();
list1.ForEach(delegate(string s) { if (!list2.Remove(s)) newlist.Add(s); });
newlist = newlist.Concat(list2).ToList();

Merge two (or more) lists into one, in C# .NET

Is it possible to convert two or more lists into one single list, in .NET using C#?
For example,
public static List<Product> GetAllProducts(int categoryId){ .... }
.
.
.
var productCollection1 = GetAllProducts(CategoryId1);
var productCollection2 = GetAllProducts(CategoryId2);
var productCollection3 = GetAllProducts(CategoryId3);
You can use the LINQ Concat and ToList methods:
var allProducts = productCollection1.Concat(productCollection2)
.Concat(productCollection3)
.ToList();
Note that there are more efficient ways to do this - the above will basically loop through all the entries, creating a dynamically sized buffer. As you can predict the size to start with, you don't need this dynamic sizing... so you could use:
var allProducts = new List<Product>(productCollection1.Count +
productCollection2.Count +
productCollection3.Count);
allProducts.AddRange(productCollection1);
allProducts.AddRange(productCollection2);
allProducts.AddRange(productCollection3);
(AddRange is special-cased for ICollection<T> for efficiency.)
I wouldn't take this approach unless you really have to though.
Assuming you want a list containing all of the products for the specified category-Ids, you can treat your query as a projection followed by a flattening operation. There's a LINQ operator that does that: SelectMany.
// implicitly List<Product>
var products = new[] { CategoryId1, CategoryId2, CategoryId3 }
.SelectMany(id => GetAllProducts(id))
.ToList();
In C# 4, you can shorten the SelectMany to: .SelectMany(GetAllProducts)
If you already have lists representing the products for each Id, then what you need is a concatenation, as others point out.
you can combine them using LINQ:
list = list1.Concat(list2).Concat(list3).ToList();
the more traditional approach of using List.AddRange() might be more efficient though.
List.AddRange will change (mutate) an existing list by adding additional elements:
list1.AddRange(list2); // list1 now also has list2's items appended to it.
Alternatively, in modern immutable style, you can project out a new list without changing the existing lists:
Concat, which presents an unordered sequence of list1's items, followed by list2's items:
var concatenated = list1.Concat(list2).ToList();
Not quite the same, Union projects a distinct sequence of items:
var distinct = list1.Union(list2).ToList();
Note that for the 'value type distinct' behaviour of Union to work on reference types, that you will need to define equality comparisons for your classes (or alternatively use the built in comparators of record types).
You could use the Concat extension method:
var result = productCollection1
.Concat(productCollection2)
.Concat(productCollection3)
.ToList();
I know this is an old question I thought I might just add my 2 cents.
If you have a List<Something>[] you can join them using Aggregate
public List<TType> Concat<TType>(params List<TType>[] lists)
{
var result = lists.Aggregate(new List<TType>(), (x, y) => x.Concat(y).ToList());
return result;
}
Hope this helps.
list4 = list1.Concat(list2).Concat(list3).ToList();
// I would make it a little bit more simple
var products = new List<List<product>> {item1, item2, item3 }.SelectMany(id => id).ToList();
This way it is a multi dimensional List and the .SelectMany() will flatten it into a IEnumerable of product then I use the .ToList() method after.
I've already commented it but I still think is a valid option, just test if in your environment is better one solution or the other. In my particular case, using source.ForEach(p => dest.Add(p)) performs better than the classic AddRange but I've not investigated why at the low level.
You can see an example code here: https://gist.github.com/mcliment/4690433
So the option would be:
var allProducts = new List<Product>(productCollection1.Count +
productCollection2.Count +
productCollection3.Count);
productCollection1.ForEach(p => allProducts.Add(p));
productCollection2.ForEach(p => allProducts.Add(p));
productCollection3.ForEach(p => allProducts.Add(p));
Test it to see if it works for you.
Disclaimer: I'm not advocating for this solution, I find Concat the most clear one. I just stated -in my discussion with Jon- that in my machine this case performs better than AddRange, but he says, with far more knowledge than I, that this does not make sense. There's the gist if you want to compare.
To merge or Combine to Lists into a One list.
There is one thing that must be true: the type of both list will be
equal.
For Example: if we have list of string so we can add add another list to the
existing list which have list of type string otherwise we can't.
Example:
class Program
{
static void Main(string[] args)
{
List<string> CustomerList_One = new List<string>
{
"James",
"Scott",
"Mark",
"John",
"Sara",
"Mary",
"William",
"Broad",
"Ben",
"Rich",
"Hack",
"Bob"
};
List<string> CustomerList_Two = new List<string>
{
"Perter",
"Parker",
"Bond",
"been",
"Bilbo",
"Cooper"
};
// Adding all contents of CustomerList_Two to CustomerList_One.
CustomerList_One.AddRange(CustomerList_Two);
// Creating another Listlist and assigning all Contents of CustomerList_One.
List<string> AllCustomers = new List<string>();
foreach (var item in CustomerList_One)
{
AllCustomers.Add(item);
}
// Removing CustomerList_One & CustomerList_Two.
CustomerList_One = null;
CustomerList_Two = null;
// CustomerList_One & CustomerList_Two -- (Garbage Collected)
GC.Collect();
Console.WriteLine("Total No. of Customers : " + AllCustomers.Count());
Console.WriteLine("-------------------------------------------------");
foreach (var customer in AllCustomers)
{
Console.WriteLine("Customer : " + customer);
}
Console.WriteLine("-------------------------------------------------");
}
}
In the special case: "All elements of List1 goes to a new List2": (e.g. a string list)
List<string> list2 = new List<string>(list1);
In this case, list2 is generated with all elements from list1.
You need to use Concat operation
When you got few list but you don't know how many exactly, use this:
listsOfProducts contains few lists filled with objects.
List<Product> productListMerged = new List<Product>();
listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
If you have an empty list and you want to merge it with a filled list, do not use Concat, use AddRange instead.
List<MyT> finalList = new ();
List<MyT> list = new List<MyT>() { a = 1, b = 2, c = 3 };
finalList.AddRange(list);

Categories