Sort List based on Array - c#

I am trying to sort a custom list based on a string array, but I am failing miserably e.g. its not sorting the list at all,
Public class CrateOrder
{
public int Id { get; set; }
public string Name { get; set; }
public Stream OrderStream { get; set; }
}
string[] selectedFruits = {"Apple", "Mango"}; // in real get from web services
var selectedFruitsList = selectedFruits.ToList();
List<CrateOrder> cFruit = GetCrateOrderFromWebServices();
var sorted = cFruit.OrderBy(s => selectedFruitsList.IndexOf(s.Name)).ToList();
It's not sorting the list properly, I want CrateOrder list item to be ordered based on selectedFruits...

If the Name property of your fruit does not match your ordering list exactly, IndexOf will always return -1. To get around this specify a case-insensitive StringComparison, but that is not available as an overload to IndexOf so you have to use a slightly more complex method:
var sorted = cFruit.OrderBy(
s => selectedFruitsList.FindIndex(
x => x.Equals(s.Name, StringComparison.OrdinalIgnoreCase)) )
.ToList();

You are almost there. The code orders the fruit from selectedFruitsList after all other items, because OrderBy order is ascending, and they are the only ones for which a non-negative number is returned.
To fix this, reverse selectedFruits, and negate the result of IndexOf, like this:
string[] selectedFruits = {"Mango", "Apple"};
...
var sorted = cFruit.OrderBy(s => -selectedFruitsList.IndexOf(s.Name)).ToList();
Alternatively, you could expand the code to deal with negative indexes explicitly:
string[] selectedFruits = {"Apple", "Mango"};
...
var sorted = cFruit.OrderBy(s => {
int index = selectedFruitsList.IndexOf(s.Name);
return index < 0 ? int.MaxValue : index;
}).ToList();

I wasn't aware if its case sensitive, can i add ignore culture to it
No - Array.IndexOf does not have parameters that let you customize the equality comparison.
Just change all of your selectedfruits and the search value to lower case:
string[] selectedFruits = {"Apple", "Mango"}; // in real get from web services
var selectedFruitsList = selectedFruits.Select(s => s.ToUpperInvariant()).ToList();
List<CrateOrder> cFruit = GetCrateOrderFromWebServices();
var sorted = cFruit.OrderBy(s => selectedFruitsList.IndexOf(s.Name.ToUpperInvariant())).ToList;

Related

Filter a list of address objects by a list of string postcodes [duplicate]

I have a list of parameters like this:
public class parameter
{
public string name {get; set;}
public string paramtype {get; set;}
public string source {get; set;}
}
IEnumerable<Parameter> parameters;
And a array of strings i want to check it against.
string[] myStrings = new string[] { "one", "two"};
I want to iterate over the parameter list and check if the source property is equal to any of the myStrings array. I can do this with nested foreach's but i would like to learn how to do it in a nicer way as i have been playing around with linq and like the extension methods on enumerable like where etc so nested foreachs just feel wrong. Is there a more elegant preferred linq/lambda/delegete way to do this.
Thanks
You could use a nested Any() for this check which is available on any Enumerable:
bool hasMatch = myStrings.Any(x => parameters.Any(y => y.source == x));
Faster performing on larger collections would be to project parameters to source and then use Intersect which internally uses a HashSet<T> so instead of O(n^2) for the first approach (the equivalent of two nested loops) you can do the check in O(n) :
bool hasMatch = parameters.Select(x => x.source)
.Intersect(myStrings)
.Any();
Also as a side comment you should capitalize your class names and property names to conform with the C# style guidelines.
Here is a sample to find if there are match elements in another list
List<int> nums1 = new List<int> { 2, 4, 6, 8, 10 };
List<int> nums2 = new List<int> { 1, 3, 6, 9, 12};
if (nums1.Any(x => nums2.Any(y => y == x)))
{
Console.WriteLine("There are equal elements");
}
else
{
Console.WriteLine("No Match Found!");
}
If both the list are too big and when we use lamda expression then it will take a long time to fetch . Better to use linq in this case to fetch parameters list:
var items = (from x in parameters
join y in myStrings on x.Source equals y
select x)
.ToList();
list1.Select(l1 => l1.Id).Intersect(list2.Select(l2 => l2.Id)).ToList();
var list1 = await _service1.GetAll();
var list2 = await _service2.GetAll();
// Create a list of Ids from list1
var list1_Ids = list1.Select(l => l.Id).ToList();
// filter list2 according to list1 Ids
var list2 = list2.Where(l => list1_Ids.Contains(l.Id)).ToList();

List.Any get matched String

FilePrefixList.Any(s => FileName.StartsWith(s))
Can I get s value here? I want to display the matched string.
Any determines only if there is a match, it doesn't return anything apart from the bool and it needs to execute the query.
You can use Where or First/FirstOrDefault:
string firstMastch = FilePrefixList.FirstOrDefault(s => FileName.StartsWith(s)); // null if no match
var allMatches = FilePrefixList.Where(s => FileName.StartsWith(s));
string firstMastch = allMatches.FirstOrDefault(); // null if no match
So Any is fine if all you need to know is if ther's a match, otherwise you can use FirstOrDefault to get the first match or null(in case of reference types).
Since Any needs to execute the query this is less efficient:
string firstMatch = null;
if(FilePrefixList.Any(s => FileName.StartsWith(s)))
{
// second execution
firstMatch = FilePrefixList.First(s => FileName.StartsWith(s));
}
If you want to put all matches into a separate collection like a List<string>:
List<string> matchList = allMatches.ToList(); // or ToArray()
If you want to output all matches you can use String.Join:
string matchingFiles = String.Join(",", allMatches);
Not with Any, no... that's only meant to determine whether there are any matches, which is why it returns bool. However, you can use FirstOrDefault with a predicate instead:
var match = FilePrefixList.FirstOrDefault(s => FileName.StartsWith(s));
if (match != null)
{
// Display the match
}
else
{
// Nothing matched
}
If you want to find all the matches, use Where instead.
if FilePrefixList is a List<string>, you can use List<T>.Find method:
string first = FilePrefixList.Find(s => FileName.StartsWith(s));
fiddle: List.Find vs LINQ (Find is faster)
List<T>.Find (MSDN) returns the first element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type T
Enumerable.Any() returns bool denoting whether any item matched the criteria.
If you need the matched item, use SingleOrDefault() instead:
var matchedPrefix = FilePrefixList.SingleOrDefault(s => FileName.StartsWith(s));
See MSDN
please check try this:
we assuming FilePrefixList is collectionlist
class A
{
public int ID { get; set; }
public string Name { get; set; }
}
List<A> FilePrefixList= new List<A>();
FilePrefixList.Add(new A
{
ID = 1,
Name = "One"
});
FilePrefixList.Add(new A
{
ID =2,
Name = "Two"
});
FilePrefixList.Add(new A
{
ID = 3,
Name = "Three"
});
select data from list is:
var listItems = FilePrefixList.Where(x =>x.Name.StartsWith("T")).ToList();

Storing the list items into a variable after filtering by using linq

I got a list of items, want to filter the list based on column distinct value(i.e based on Level) and also after filtering need to get the count and store them as an int variable.
Can anyone please help me.
**List**
Public Class Totalitems
{
public string ItemName;
public string ItemId;
public string ItemGroup;
public int Level;
}
Id= "123asd";
List<Totalitems> l_items = this.getslist(Id);
/*How to filter based on distinct level */
/* var filteredItems = (
from p in l_items
select p.Level)
.Distinct(); */
**Finally:**
//Stores the elements contained in the List into a variable
int totalItemsafterFiltering = l_FilteredItems.Count;
You want to use GroupBy for this task:
var numberOfDifferentLevels = l_items.GroupBy(x => x.Level).Count();
GroupBy is especially useful, if you want to do something with the actual elements in the group. For example, you might want to know how many items per level there are:
var itemsPerLevel = l_items.GroupBy(x => x.Level)
.Select(x => new { Level = x.Key,
NumberOfItems = x.Count() });
Another approach when you really only care about the number of distinct levels, is the following:
var numberOfDifferentLevels = l_items.Select(x => x.Level).Distinct().Count();

Sort IEnumerable<Object> by property and ordered array of those properties

Given the following:
public class Foo
{
/* other properties */
public Int32 Id { get; set; }
}
var listOfFoo = new[]{
new Foo { Id = 1 },
new Foo { Id = 2 },
new Foo { Id = 3 }
};
var sortOrderIds = new[]{
2, 3, 1
};
If I wanted to sort listOfFoo to have the Ids end up in the same order as presented in sortOrderIds, what's the best way? I assume I could sort using something like:
Int32 SortUsingIdArrayAsReference(Foo x, Foo y)
{
// creative license on "IndexOf", bear with me
return Int32.CompareTo(sortOrderids.IndexOf(x.Id), sortOrderIds.indexOf(y.Id));
}
But is that really the best way to do this? I was hoping LINQ may have something better I could use, but if not oh well. Just looking for other input and see if anyone else has a better way.
You can use List.IndexOf
var ordered = listOfFoo.OrderBy(o => sortOrderIDs.IndexOf(o.Id));
Edit: Since sortOrderIDs is an array:
var ordered = listOfFoo.OrderBy(o => Array.IndexOf(sortOrderIds, o.Id));
Or, if you want to use the same for lists and arrays, cast it to IList:
var ordered = listOfFoo.OrderBy(o => ((IList)sortOrderIds).IndexOf(o.Id));
You could use something like this:
var ordered = listOfFoo.OrderBy(x => Array.IndexOf(sortOrderIds, x.Id));
This would sort them according to the order of the IDs in sortOrderIds. Foo objects whose IDs are not found will be at the very top of the resulting list.
If you want them to be at the bottom, change the code like this:
var ordered = listOfFoo.OrderBy(x =>
{
var idx = Array.IndexOf(sortOrderIds, x.Id);
return idx == -1 ? int.MaxValue : idx;
});

How to match the results back to an array

I have an array of objects. The object has two properties a value and an index.
I use a linq to entities query with the contains keyword to bring back all results in a table that match up to value.
Now here is the issue... I want to match up the results to the object index...
what is the fastest best way to perform this. I can add properties to the object.
It is almost like I want the query results to return this:
index = 1;
value = "searchkey"
queryvalue = "query value"
From your question I think I can assume that you have the following variables defined:
Lookup[] (You look-up array)
IEnumerable<Record> (The results returned by your query)
... and the types look roughly like this:
public class Lookup
{
public int Index { get; set; }
public int Value { get; set; }
}
public class Record
{
public int Value { get; set; }
/* plus other fields */
}
Then you can solve your problem in a couple of ways.
First using an anonymous type:
var matches
= from r in records
join l in lookups on r.Value equals l.Value
group r by l.Index into grs
select new
{
Index = grs.Key,
Records = grs.ToArray(),
};
The other two just use standard LINQ GroupBy & ToLookup:
IEnumerable<IGrouping<int, Record>> matches2
= from r in records
join l in lookups on r.Value equals l.Value
group r by l.Index;
ILookup<int, Record[]> matches3
= matches2.ToLookup(m => m.Key, m => m.ToArray());
Do these solve your problem?
Just a shot in the dark as to what you need, but the LINQ extension methods can handle the index as a second paramter to the lambda functions. IE:
someCollection.Select( (x,i) => new { SomeProperty = x.Property, Index = i } );

Categories