Rearrange a list based on given order in c# - c#

I have a list as follows:
{CT, MA, VA, NY}
I submit this list to a function and I get the optimum waypoint order list
{2,0,1,3}
Now I have to rearrange the list as per the order that is newly provided. i.e. after rearranging, the list should look like:
{VA, CT, MA, NY}
What is the optimum way to do it? Using linq is there a way?

You could try the following:
var list = new List<string>{"CT", "MA", "VA", "NY"};
var order = new List<int>{2, 0, 1, 3};
var result = order.Select(i => list[i]).ToList();

This seems like the simplest approach:
oldItems = LoadItems(); //{"CT","MA","VA","NY"};
List<string> newItems = List<string>();
foreach(int idx in returnedIndexes)
{
newItems.Add(oldItems[idx]);
}

Related

How to compare two List<string> and return non-matches (c#)

.net fiddle: https://dotnetfiddle.net/27wJLc
I have two lists of strings, the 'all' list and a list to compare, I then want to return the strings from the 'All' list that did not match:
public static void Main()
{
List<string> ratiodGids = new List<string>();
List<string> allGids = new List<string>();
List<string> notRatiodGids = new List<string>();
allGids.Add("tom");
allGids.Add("bob");
allGids.Add("bill");
allGids.Add("tim");
allGids.Add("sam");
ratiodGids.Add("tom");
ratiodGids.Add("tim");
ratiodGids.Add("sam");
foreach(var g in ratiodGids)
{
if(!allGids.Contains(g))
{
notRatiodGids.Add(g);
}
}
Console.WriteLine(notRatiodGids.Count);
foreach(var i in notRatiodGids)
Console.WriteLine(i);
}
I'm pretty sure I can re-type everything back to IEnumerable then use Intersect or Except, but that will take some work. So I wanted to see if it was possible with Lists before doing that.
With the code I have, the list returns nothing. If I remove the '!' I get back tom, tim, sam. So it works one way, but not the other.
UPDATE: The result I want is just the strings from ratiodGids that don't exist in allGids. Order does not matter. Also, I have tried using Exists(), Except(), Any(), Where() and they both give me the "List does not contain a method called blah ...".
Well, you can use Except() on a List as well, because it implements IList which derives from IEnumerable.
Update
As pointed out in the comments, the Linq-methods are extension methods on IEnumerable. To use them, you have to add their namespace by adding: using System.Linq;
You can compare two lists and return a new list with only the differences like so:
List<string> difference = list1.Except(list2).ToList();
You can do it like:
var results = allGids.Where( a=> !ratiodGids.Contains(a))
if you want to get uniqe items from both lists then:
var results = allGids.Where( a=> !ratiodGids.Contains(a))
.Union(ratiodGids.Where( a=> !allGids.Contains(a)));
change this
foreach(var g in ratiodGids)
{
if(!allGids.Contains(g))
{
notRatiodGids.Add(g);
}
}
to this
foreach(var g in allGids)
{
if(!ratioGids.Contains(g))
{
notRatiodGids.Add(g);
}
}
To be clear, if listA = { A,B,C } and listB = { B,C,D } do you want your result to be { A,D }? If so, here's one way:
var union = listA.Union(listB);
var intersection = listA.Intersect(listB);
var outside = union.Except(intersection);

LINQ Group By along with percent of list that contains element

Example is probably the easiest way to explain:
{1,2,2,3}
becomes
{
{1,0.25}
{2,0.5}
{3,0.25}
}
I'm thinking I'd do the inner pairs using a simple struct. I understand using GroupBy and everything, but is there a way to create a new list like this in one big LINQ statement?
another example:
{'a','g','a','a','f'}
becomes
{
{'a',0.6}
{'g',0.2}
{'f',0.2}
}
You want something like this
[TestMethod]
public void T()
{
var mySet = new List<string> { "a", "b", "a" };
var set = from i in mySet
group i by i into g
select new { Item = g.Key, Percentage = ((double)g.Count()) / mySet.Count() };
Assert.AreEqual(2, set.Count());
Assert.AreEqual("a", set.First().Item);
Assert.AreEqual(2.0/3, set.First().Percentage);
}

check whether a List<string> contains an element in another List<string> using LINQ

How do I check whether a List contains an element that exists in another List using LINQ in C#? I don't want to use a for/while loop.
So, if List1 has A, B, C and List2 has B, 1, 2, then I would return true.
Try this:
List<string> a = ...
List<string> b = ...
var inComon = a.Intersect(b).Any();
Use Enumerable.Any Method:
List<string> l1 = new List<string> { "1", "2" };
List<string> l2 = new List<string> { "1", "3" };
var result = l2.Any(s => l1.Contains(s));
I'd say the Intersect method (see answer by dasblinkenlight) + Any must work better than Contains + Any. It is definetely better to use Any than Count.

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);

How do I copy items from list to list without foreach?

How do I transfer the items contained in one List to another in C# without using foreach?
You could try this:
List<Int32> copy = new List<Int32>(original);
or if you're using C# 3 and .NET 3.5, with Linq, you can do this:
List<Int32> copy = original.ToList();
I see that this answer is still getting upvotes. Well, here's a secret for ya: the above answer is still using a foreach. Please don't upvote this any further.
To add the contents of one list to another list which already exists, you can use:
targetList.AddRange(sourceList);
If you're just wanting to create a new copy of the list, see the top answer.
For a list of elements
List<string> lstTest = new List<string>();
lstTest.Add("test1");
lstTest.Add("test2");
lstTest.Add("test3");
lstTest.Add("test4");
lstTest.Add("test5");
lstTest.Add("test6");
If you want to copy all the elements
List<string> lstNew = new List<string>();
lstNew.AddRange(lstTest);
If you want to copy the first 3 elements
List<string> lstNew = lstTest.GetRange(0, 3);
And this is if copying a single property to another list is needed:
targetList.AddRange(sourceList.Select(i => i.NeededProperty));
This method will create a copy of your list but your type should be serializable.
Use:
List<Student> lstStudent = db.Students.Where(s => s.DOB < DateTime.Now).ToList().CopyList();
Method:
public static List<T> CopyList<T>(this List<T> lst)
{
List<T> lstCopy = new List<T>();
foreach (var item in lst)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, item);
stream.Position = 0;
lstCopy.Add((T)formatter.Deserialize(stream));
}
}
return lstCopy;
}
Easy to map different set of list by linq without for loop
var List1= new List<Entities1>();
var List2= new List<Entities2>();
var List2 = List1.Select(p => new Entities2
{
EntityCode = p.EntityCode,
EntityId = p.EntityId,
EntityName = p.EntityName
}).ToList();
Adding to the top answers, if you want copies of "the objects in the list", then you can use Select and make the copies. (While the other answers make "a copy of a list", this answer makes "a list of copies").
Suppose your item has a Copy method:
List<MyObject> newList = oldList.Select(item => item.Copy()).ToList();
Or that you can create a new object from the previous one with a constructor:
List<MyObject> newList = oldList.Select(item => new MyObject(item)).ToList();
The result of Select is an IEnumerable<MyObject> that you can also pass to AddRange for instance, if your goal is to add to an existing list.
OK this is working well
From the suggestions above GetRange( ) does not work for me with a list as an argument...so sweetening things up a bit from posts above: ( thanks everyone :)
/* Where __strBuf is a string list used as a dumping ground for data */
public List < string > pullStrLst( )
{
List < string > lst;
lst = __strBuf.GetRange( 0, __strBuf.Count );
__strBuf.Clear( );
return( lst );
}
public static List<string> GetClone(this List<string> source)
{
return source.Select(item => (string)item.Clone()).ToList();
}
Here another method but it is little worse compare to other.
List<int> i=original.Take(original.count).ToList();

Categories