I have this List defined as property :
List<string> colors= new List<string>();
colors.Add("Red");
colors.Add("Blue");
colors.Add("Green");
colors.Add("Black");
And I have this function:
private List<string> getColors(string colorName , List<string> headers)
{
List<string> list2return = colors(return all colors except red and black);
return list2return ;
}
My question is how can I select from list all items except red and black?
Like this?:
colors.Where(c => !c.Equals("Red") && !c.Equals("Black")).ToList()
Or, if you need it to be case-insensitive:
colors.Where(c =>
!c.Equals("Red", StringComparison.InvariantCultureIgnoreCase) &&
!c.Equals("Black", StringComparison.InvariantCultureIgnoreCase)
).ToList()
(Though it's not really clear why that method has parameters which aren't being used. Or how it has access to the colors variable in the first place, since that really doesn't look like a class-level member.)
Another way is like this:
colors.Except(new[] { "Red", "Black" });
Related
I have a question:
For example I have 2 lists:
1: Banana Apple Orange
2: Yellow Red Orange
I want it to list.sort
so it will be:
Apple Banana Orange
But in the same time I want the SAME changes happening inside of the Yellow red orange list.
So it would be like this:
Apple Banana Orange
Red Yellow Orange
I didnt try this because I literally have no idea how to do this and all this is just on the planning board
You should really start using classes which encapsulate these information in one entity, for example:
public class Fruit
{
public string Name {get; set;}
public string Color {get; set;}
}
Then it's easy and readable:
List<Fruit> fruits = new()
{
new Fruit{ Name = "Banana", Color = "Yellow" },
new Fruit{ Name = "Apple", Color = "Red" },
new Fruit{ Name = "Orange", Color = "Orange" }
};
var orderedFruits = fruits.OrderBy(f => f.Name);
If you want to sort the original list, simply append ToList ad re-assign it:
fruits = fruits.OrderBy(f => f.Name).ToList();
If you really must use two separate lists which depend on each other, you could use Zip:
List<string> fruitNames = new() { "Banana", "Apple", "Orange" };
List<string> fruitColors = new() { "Yellow", "Red", "Orange" };
List<(string Name, string Color)> orderedFruits = fruitNames
.Zip(fruitColors, (n, c) => (Name: n, Color: c))
.OrderBy(x => x.Name)
.ToList();
fruitNames = orderedFruits.Select(x => x.Name).ToList();
fruitColors = orderedFruits.Select(x => x.Color).ToList();
If for some reason it is problematic to merge the lists, say you have a large list of large structs for example, an alternative can be to use a proxy list with indices for sorting:
var fruits = ...
var colors = ...
var proxy = Enumerable.Range(0, fruits.Length);
var sortedProxy = proxy.OrderBy(i => fruits[i]);
var sortedColors = sortedProxy.Select(i => colors[i]).ToList();
This lets you sort the indices according to the order defined by one list, and then you can use this ordering to create an ordered list for any list of the same length. Note that this example leaves the fruit list unordered.
You can reduce allocations by creating a custom comparer that does the i => fruits[i]-part so that you can use List.Sort instead of OrderBy.
You could try to zip the two list, making a list of pairs.
Then you can apply a custom sort on this list of pair, sorting pairs only relatively to the first element.
Once the sort is done, you can unzip the list of pairs to get back your two lists, sorted the same way.
I need to find out how to sort some data by pre-defined pattern.
Lets say I have some strings, which represents product informafion, e. g.
Product1, red, 70/n
Product6, blue, 90/n
Product3, red, 50/n
Product9, white, 33/n
I separated these strings by coma string split and stored them at different Arrays (name, color, price) and then DataTable with same columns.
I can order created rows by color using :
DataView.sort = "color"
or by LINQ with
DataRow[] dr = table.Select().OrderBy(u=>u[color]).ToArray();
DataTable sortedtable = dr.CopyToDataTable();
However this is just simple sorting, asc/desc, based on alphabet.
I would like to achieve sorting with pre-defined pattern. In example the item order would be defined by colors in order red, black, blue, white.
Is there anything simple I could do? I think this is possible with checking each row color and comparing it with predefined color list, then building new Array / DataTable based on this order. However I feel that this is weak approach.
You could store the order in another collection and then use IndexOf:
var colorOrderList = new List<string>{"red", "black", "blue", "white"};
table = table.AsEnumerable()
.OrderBy(row => colorOrderList.IndexOf(row.Field<string>("color")))
.CopyToDataTable();
You can define n ordering array like
var order = new [] { "red", "blue", "white"};
and then use IndexOf
DataRow.Select().OrderBy(u=>Array.IndexOf(order, u[color]))
Use IComparable to make a custom sort order.
https://support.microsoft.com/en-ca/help/320727/how-to-use-the-icomparable-and-icomparer-interfaces-in-visual-c
Products.OrderBy(u => u == "Red" ? 0 : u == "Black" ? 1 : u == "Blue" ? 2 : 3)
has the advantage that it should be translatable to a SQL statement so that the database server can do the sorting.
You can use IComaparable. First create a custom class which can accommodate your product details. Make this class implement the IComparable interface.
public class ProductDetails : IComparable
{
public int ProductId { get; set; }
public int CompareTo(object obj)
{
ProductDetails prodDetails = obj as ProductDetails;
if (obj == null) return 1;
if (prodDetails != null)
{
if (this.ProductId < prodDetails.ProductId) return 1;
else
return 0;
}
else {
return 0;
}
}
}
I have a list:
List<string> strlist=new List<string>();
strlist.add("UK");
strlist.add("US");
strlist.add("India");
strlist.add("Australia");
I want to change the index of some elements in the list:
The current index of "US" is 1, I want to replace it with 3 and for "Australia" it should be 1.
If you know the indices already, you can just use 'swap' them:
var temp = strlist[1];
strlist[1] = strlist[3];
strlist[3] = temp;
It seems to me that you really just want to sort the list.
If so, just do this:
strlist.Sort().
This will work for a plain list of strings, because string defines a suitable comparison operator that Sort() can use.
If you want to keep "UK" at the start of the list and sort the rest of the list, you can do so like this:
strlist.Sort(1, strlist.Count-1, null);
The above line will result in the list being:
UK
Australia
India
US
Try this:
Use a swapping variable
String app = strlist[1];
strlist[1] = strlist[3];
strlist[3] = app;
List<string> can be indexed using list[index] = .... That if you want to replace items manually. Otherwise (I'm guessing), if you need to sort the list alphabetically, but leave the "UK" at the top then you need to do sorting:
var list = new List<string> {"UK", "US", "India", "Australia" };
list.Sort((a, b) =>
{
if (a == "UK")
return -1;
if (b == "UK")
return 1;
return string.Compare(a, b, StringComparison.CurrentCulture);
});
I am desperately trying to understand linq and now I have a concrete example of what I want to do (and fail):
Console.WriteLine("{0}", (from myaddresses[x].PostalNr where x => myaddresses[x].SortType == "110" ))
myaddress is a dictionary of OneAddress objects (my own object) and that object contains the properties SortType and PostalNr.
I thought I didn't need a loop to do the above, but when the above is rewritten to work it might only take the first hit it gets or?
The questions I want to perform is:
For each entry in the dictionary that has SortType set to 110, print out it's postal number.
Below is a step-by-step walkthrough of one approach to this.
To setup the sample data (based on your question) we have the OneAddress class:
class OneAddress
{
public string PostalNr { get; set; }
public string SortType { get; set; }
}
This is in a Dictionary so we then have:
var myAddresses = new Dictionary<int, OneAddress>();
myAddresses.Add(1, new OneAddress() { PostalNr = "123", SortType = "101" });
myAddresses.Add(2, new OneAddress() { PostalNr = "124", SortType = "110" });
myAddresses.Add(3, new OneAddress() { PostalNr = "125", SortType = "101" });
myAddresses.Add(4, new OneAddress() { PostalNr = "126", SortType = "110" });
myAddresses.Add(5, new OneAddress() { PostalNr = "127", SortType = "110" });
First, a basic Linq query to get all dictionary entries:
var results = from a in myAddresses
select a;
This returns an IEnumerable<T> where T is a KeyValuePair<int, OneAddress> (same as our Dictionary).
As stated, you only want the PostalNr not the KeyValuePair so we change our query to:
var results = from a in myAddresses
select a.Value.PostalNr;
The Value contains the OneAddress object, and we get only the property we need (in an IEnumerable<T>).
However this is for all items in the collection; we can now add our filter.
var results = from a in myAddresses
where a.Value.SortType == "110"
select a.Value.PostalNr;
Now we're getting the PostalNr for any OneAddress in the Dictionary where SortType is "110", and that only leaves printing the results to the console screen.
As highlighted in other answers, Console.WriteLine() doesn't work with an enumerable list of strings, so we can enumerate the items with:
foreach (string postalNr in results)
{
Console.WriteLine(postalNr);
}
Or (if we're using System.Collections.Generic) we can do it on one line with:
results.ToList().ForEach(p => Console.WriteLine(p));
The LINQ query you're looking for takes the form:-
from <item> in <collection> where <item.someclause> select <item.targetfield>
That will return an IEnumerable<targetfieldtype> which Console.WriteLine doesn't handle.
If the type is a string as it is here you can then apply string.join() to concatenate it into a single string.
Like this:-
Console.WriteLine
(
string.Join
(
"\r\n",
from address in Addresses
where address.SortType=="110"
select address.PostalNr
)
);
I can't add a comment, because my reputation is to low.
Your problem is not linq itself but how you use it. What you are doing is printing one line. This line will contain the value of the ToString call on a list/collection/linq result. I think it might help you if you don't try to put everything in one line. The error should be obvious if you extract the linq query from the write line.
I don't have an IDE here at my workplace, so I can't confirm my code works, but what you want to do is basically:
var addresses = (from myaddresses[x].PostalNr where x => myaddresses[x].SortType == "110" )
addresses.each(Console.WriteLine)
I have a dictionary with a list of strings that each look something like:
"beginning|middle|middle2|end"
Now what I wanted was to do this:
List<string> stringsWithPipes = new List<string>();
stringWithPipes.Add("beginning|middle|middle2|end");
...
if(stringWithPipes.Contains("beginning|middle|middle2|end")
{
return true;
}
problem is, the string i'm comparing it against is built slightly different so it ends up being more like:
if(stringWithPipes.Contains(beginning|middle2|middle||end)
{
return true;
}
and obviously this ends up being false. However, I want to consider it true, since its only the order that is different.
What can I do?
You can split your string on | and then split the string to be compared, and then use Enumerable.Except along with Enumerable.Any like
List<string> stringsWithPipes = new List<string>();
stringsWithPipes.Add("beginning|middle|middle2|end");
stringsWithPipes.Add("beginning|middle|middle3|end");
stringsWithPipes.Add("beginning|middle2|middle|end");
var array = stringsWithPipes.Select(r => r.Split('|')).ToArray();
string str = "beginning|middle2|middle|end";
var compareArray = str.Split('|');
foreach (var subArray in array)
{
if (!subArray.Except(compareArray).Any())
{
//Exists
Console.WriteLine("Item exists");
break;
}
}
This can surely be optimized, but the above is one way to do it.
Try this instead::
if(stringWithPipes.Any(P => P.split('|')
.All(K => "beginning|middle2|middle|end".split('|')
.contains(K)))
Hope this will help !!
You need to split on a delimeter:
var searchString = "beginning|middle|middle2|end";
var searchList = searchString.Split('|');
var stringsWithPipes = new List<string>();
stringsWithPipes.Add("beginning|middle|middle2|end");
...
return stringsWithPipes.Select(x => x.Split('|')).Any(x => Match(searchList,x));
Then you can implement match in multiple ways
First up must contain all the search phrases but could include others.
bool Match(string[] search, string[] match) {
return search.All(x => match.Contains(x));
}
Or must be all the search phrases cannot include others.
bool Match(string[] search, string[] match) {
return search.All(x => match.Contains(x)) && search.Length == match.Length;
}
That should work.
List<string> stringsWithPipes = new List<string>();
stringsWithPipes.Add("beginning|middle|middle2|end");
string[] stringToVerifyWith = "beginning|middle2|middle||end".Split(new[] { '|' },
StringSplitOptions.RemoveEmptyEntries);
if (stringsWithPipes.Any(s => !s.Split('|').Except(stringToVerifyWith).Any()))
{
return true;
}
The Split will remove any empty entries created by the doubles |. You then check what's left if you remove every common element with the Except method. If there's nothing left (the ! [...] .Any(), .Count() == 0 would be valid too), they both contain the same elements.