I have list with items and I check if an item exists in a List. If it exists, I try to find it.
I think that it has a little overhead, because I currently make two passes over the list. Is it possible to do in single pass?
Currently I have.
public partial class Item
{
public string text;
public int id;
}
....
static List<Item> data = new List<Item>();
static stub = new Item() { text = "NaN", id = -1 };
public static Item Get(int targetId)
{
if (data.Any(f => f.id == targetId) == false)
{
return stub;
}
return data.Find(f => f.id == targetId);
}
I want something like
...
public static Item Get(int targetId)
{
Item result;
result = data.Find(f => f.id == targetId);
if (result == null)
{
return stub;
}
return result;
}
You seem to be looking for FirstOrDefault():
Item _stub = new Item
{
text = "NaN",
id = -1
};
public Item FindByID(int id)
{
// Find the first item that has the provided id, or null if none exist.
var existingItem = data.FirstOrDefault(i => i.id == id);
// When not found, return the _stub
return existingItem ?? _stub;
}
You also may want to reconsider your naming conventions and whether you actually need these members to be static.
You can use List.FindIndex:
public static Item get(int i)
{
int index = data.FindIndex(item => item.id == i);
if (index == -1) return stub;
return data[index];
}
If it's actually an array you can use Array.FindIndex:
public static Item get(int i)
{
int index = Array.FindIndex(data, item => item.id == i);
if (index == -1) return stub;
return data[index];
}
So FirstOrDefault() is the way to go. You can also use SingleOrDefault() if there is only supposed to be one item in that list.
static stub = new Item() { text = "NaN", id = -1 };
public static Item get(int i)
{
Item result;
result = data.FirstOrDefault(f => f.id == i);
if (result == null)
{
return stub;
}
return result;
}
I use an extension method for exactly this purpose
public static T FirstOrSpecificDefault<T>(this IEnumerable<T> list,
Func<T, bool> predicate, T defaultValue)
{
return list.FirstOrDefault(predicate) ?? defaultValue;
}
usage in your case would be
Item result = list.FirstOrSpecificDefault(f => f.id == i, stub);
I think you can try this:
return data.Find(f => f.id == i) ?? stub;
Related
So, I am new to LINQ and trying to figure out how to filter items.
That’s my task
public async Task<PagedList<Item>> GetItems (ItemParams itemParams) {
var items = _context.Items.AsQueryable ();
if (itemParams.CategoryId > 0) {
var category = GetCategory (itemParams.CategoryId);
items = items.Where (i => FilterItems (i, category.Result));
}
return await PagedList<Item>.CreatAsync (items, itemParams.PageNumber, itemParams.PageSize);
}
and the function that decides which items to return is
static bool FilterItems (Item item, Category category) {
if (item.CategoryId == category.Id) {
return true;
}
if (category.Children.Count > 0) {
foreach (Category cat in category.Children) {
return FilterItems (item, cat);
}
}
return false;
}
the get category func
public async Task<Category> GetCategory (int? id) {
if (id == null) {
return null;
}
var categories = _context.Categories.Include (x => x.Children).AsEnumerable ().Where (c => c.Id == id);
categories = Traverse (categories);
var category = await Task.FromResult (categories.First (c => c.Id == id));
return category;
}
Your filter will not work as expected, as the foreach returns at the first loop. Also the name FilterItems is not intuitive.
static bool ContainsItem(Category category, Item item)
{
return
category.Id == item.CategoryId ||
category.Children.Any(c => ContainsItem(c, item);
}
Since C# performs a short-circuit evaluation of the || Operator, the second term will not be evaluated if the first matches. Note that this is not a dirty trick, but is part of the C# specification.
From the code, it seems FilterItems() returns true if item is in current category or any sub-categories?
If that's the case, I think your current code has a problem: the recursive call to FilterItems() returns result immediately for the first sub-category without checking other sub-categories
Here is the modified code:
public static bool InCategoryOrSubCategory(Item item, Category category)
{
return item.CategoryId == category.Id ||
category.Children.Any(subCategory => InCategoryOrSubCategory(item, subCategory));
}
I change the name to InCategoryOrSubCategory to make it clearer
Here's a curried version, to make the Where call slightly nicer (but the method itself is a bit more cryptic):
public static Func<Item, bool> InCategoryOrSubCategory(Category category)
{
return item =>
item.CategoryId == category.Id ||
category.Children.Any(subCategory => InCategoryOrSubCategory(subCategory)(item));
}
Usage:
items.Where(InCategoryOrSubCategory(category))
I want an alphabetic sort with one exception.
There is a Group with a Name = "Public" and an ID = "0" that I want first.
(would rather use ID = 0)
After that then sort the rest by Name.
This does not return public first.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.Name);
}
}
What I want is:
return GroupAuthoritys.Where(x => x.ID == 0)
UNION
GroupAuthoritys.Where(x => x.ID > 0).OrderBy(x => x.Group.Name);
GroupAuthority has a public property Group and Group has Public properties ID and Name.
I used basically the accepted answer
using System.ComponentModel;
namespace SortCustom
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TestSort();
}
private void TestSort()
{
List<CustomSort> LCS = new List<CustomSort>();
LCS.Add(new CustomSort(5, "sss"));
LCS.Add(new CustomSort(6, "xxx"));
LCS.Add(new CustomSort(4, "xxx"));
LCS.Add(new CustomSort(3, "aaa"));
LCS.Add(new CustomSort(7, "bbb"));
LCS.Add(new CustomSort(0, "pub"));
LCS.Add(new CustomSort(2, "eee"));
LCS.Add(new CustomSort(3, "www"));
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
LCS.Sort();
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
}
}
public class CustomSort : Object, INotifyPropertyChanged, IComparable<CustomSort>
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, e);
}
private Int16 id;
private string name;
public Int16 ID { get { return id; } }
public String Name { get { return name; } }
public int CompareTo(CustomSort obj)
{
if (this.ID == 0) return -1;
if (obj == null) return 1;
if (obj is CustomSort)
{
CustomSort comp = (CustomSort)obj;
if (comp.ID == 0) return 1;
return string.Compare(this.Name, comp.Name, true);
}
else
{
return 1;
}
}
public override bool Equals(Object obj)
{
// Check for null values and compare run-time types.
if (obj == null) return false;
if (!(obj is CustomSort)) return false;
CustomSort comp = (CustomSort)obj;
return (comp.ID == this.ID);
}
public override int GetHashCode()
{
return (Int32)ID;
}
public CustomSort(Int16 ID, String Name)
{
id = ID;
name = Name;
}
}
}
You need to use a comparison function, they are functions that from two instances of your type return an integer that return 0 if both are equals, a negative value if the first is less than the second and a positive value if the first is greater than the second.
MSDN has a nice table that is easier to follow than text (StackOverflow still doesn't support tables in 2014)
IComparer<T>
Most sort methods accept a custom comparer implementation of type IComparer<T> you should create one encapsulating your custom rules for Group :
class GroupComparer : IComparer<Group>
{
public int Compare(Group a, Group b)
{
if (a != null && b != null && (a.Id == 0 || b.Id == 0))
{
if (a.Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return a.Id == 0 ? -1 : 1;
}
if (a == null || b == null)
{
if (ReferenceEquals(a, b))
{
return 0;
}
return a == null ? -1 : 1;
}
return Comparer<string>.Default.Compare(a.Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _, new GroupAuthorityComparer());
IComparable<T>
If it is the only way to compare Group instances you should make it implement IComparable<T> so that no aditional code is needed if anyone want to sort your class :
class Group : IComparable<Group>
{
...
public int CompareTo(Group b)
{
if (b != null && (Id == 0 || b.Id == 0))
{
if (Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return Id == 0 ? -1 : 1;
}
return Comparer<string>.Default.Compare(Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _.Group);
The choice between one way or the other should be done depending on where this specific comparer is used: Is it the main ordering for this type of item or just the ordering that should be used in one specific case, for example only in some administrative view.
You can even go one level up and provide an IComparable<GroupAuthority> implementation (It's easy once Group implement IComparable<Group>):
class GroupAuthority : IComparable<GroupAuthority>
{
...
public int CompareTo(GroupAuthority b)
{
return Comparer<Group>.Default.Compare(Group, b.Group);
}
}
Usage:
items.OrderBy(_ => _);
The advantage of the last one is that it will be used automatically, so code like: GroupAuthoritys.ToList().Sort() will do the correct thing out of the box.
You can try something like this
list.Sort((x, y) =>
{
if (x.Id == 0)
{
return -1;
}
if (y.Id == 0)
{
return 1;
}
return x.Group.Name.CompareTo(y.Group.Name);
});
Where list is List<T>.
This method takes advantage of custom sort option provided by List<T> using Comparison<T> delegate.
Basically what this method does is, it just adds special condition for comparison when Id, If it is zero it will return a value indicating the object is smaller which makes the object to come in top of the list. If not, it sorts the object using its Group.Name property in ascending order.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.ID == 0)
.ThenBy(x => x.Group.Name);
}
}
I have this validator class:
internal class CustomerTypeValidator : AbstractValidator<CustomerType>
{
public CustomerTypeValidator()
{
RuleFor(x => x.Number).Must(BeANumber).WithState(x => CustomerTypeError.NoNumber);
}
private bool BeANumber(string number)
{
int temp;
bool ok = int.TryParse(number, out temp);
return ok && temp > 0;
}
}
And I have the service class:
public class CustomerTypeService
{
public CustomerType Save(CustomerType customerType)
{
ValidationResult results = Validate(customerType);
if (results != null && !results.IsValid)
{
throw new ValidationException<CustomerTypeError>(results.Errors);
}
//Save to DB here....
return customerType;
}
public bool IsNumberUnique(CustomerType customerType)
{
var result = customerTypeRepository.SearchFor(x => x.Number == customerType.Number).Where(x => x.Id != customerType.Id).FirstOrDefault();
return result == null;
}
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x).Must(IsNumberUnique).WithState(x => CustomerTypeError.NumberNotUnique);
return validator.Validate(customerType);
}
}
However I get the following exception:
Property name could not be automatically determined for expression x => x. Please specify either a custom property name by calling 'WithName'.
Is the above not the correct way to add an extra rule?
With the current version of FluentValidation, it is possible to solve the above problem by doing the following:
public bool IsNumberUnique(CustomerType customerType, int id)
{
var result = customerTypeRepository.SearchFor(x => x.Number == customerType.Number).Where(x => x.Id != customerType.Id).FirstOrDefault();
return result == null;
}
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x.Id).Must(IsNumberUnique).WithState(x => CustomerTypeError.NumberNotUnique);
return validator.Validate(customerType);
}
So I have a function that I pass Func call back to. I would also like to add some sort of selection projection to be able to do the projection on the object, meaning I would only perform one database call. The function looks something like this:
public T Get<T>(string id, Func<T> getItemCallback) where T : class
{
item = getItemCallback();
if (item != null)
{
doSomeThing(item);
// Here I would like to call something else that is
// expecting a specific type. Is there way to pass in a
// dynamic selector?
doSomethingElse(item.Select(x => new CustomType { id = x.id, name = x.name }).ToList());
}
return item;
}
void doSomethingElse(List<CustomType> custom)
{
....
}
Leme show how I cam currently calling this perhaps that will help:
public List<MyDataSet> GetData(string keywords, string id)
{
return _myObject.Get(
id,
() => db.GetDataSet(keywords, id).ToList());
// Perhaps I could add another parameter here to
// handled the projection ????
}
Thanks to Reed I figured it out...would look like this:
public T Get<T>(string id, Func<T> getItemCallback, Func<T, List<CustomType>> selector) where T : class
{
item = getItemCallback();
if (item != null)
{
doSomething(item);
var custom = selector(item);
if (custom != null)
{
doSomethingElse(custom);
}
}
return item;
}
And The call looks like:
public List<MyDataSet> GetData(string keywords, string id)
{
return _myObject.Get(
id,
() => db.GetDataSet(keywords, id).ToList(),
x => x.Select(d => new CustomType { id = d.ReferenceId, name = d.Name })
.ToList());
}
You would need to also pass in a conversion function:
public T Get<T>(string id, Func<T> getItemCallback, Func<T, List<CustomType>> conversion) where T : class
{
item = getItemCallback();
if (item != null)
{
doSomeThing(item);
if (conversion != null)
doSomethingElse(conversion(item));
}
return item;
}
I'm having a List of String like
List<string> MyList = new List<string>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
};
I need to remove duplicate from the List i.e, if "A-B" and "B-A" exist then i need to keep only "A-B" (First entry)
So the result will be like
"A-B"
"C-D"
"C-E"
"D-E"
"F-G"
Is there any way to do this using LINQ?
Implement IEqualityComparer witch returns true on Equals("A-B", "B-A"). And use Enumerable.Distinct method
This returns the sequence you look for:
var result = MyList
.Select(s => s.Split('-').OrderBy(s1 => s1))
.Select(a => string.Join("-", a.ToArray()))
.Distinct();
foreach (var str in result)
{
Console.WriteLine(str);
}
In short: split each string on the - character into two-element arrays. Sort each array, and join them back together. Then you can simply use Distinct to get the unique values.
Update: when thinking a bit more, I realized that you can easily remove one of the Select calls:
var result = MyList
.Select(s => string.Join("-", s.Split('-').OrderBy(s1 => s1).ToArray()))
.Distinct();
Disclaimer: this solution will always keep the value "A-B" over "B-A", regardless of the order in which the appear in the original sequence.
You can use the Enumerable.Distinct(IEnumerable<TSource>, IEqualityComparer<TSource>) overload.
Now you just need to implement IEqualityComparer. Here's something for you to get started:
class Comparer : IEqualityComparer<String>
{
public bool Equals(String s1, String s2)
{
// will need to test for nullity
return Reverse(s1).Equals(s2);
}
public int GetHashCode(String s)
{
// will have to implement this
}
}
For a Reverse() implementation, see this question
You need to implement the IEqualityComparer like this:
public class CharComparer : IEqualityComparer<string>
{
#region IEqualityComparer<string> Members
public bool Equals(string x, string y)
{
if (x == y)
return true;
if (x.Length == 3 && y.Length == 3)
{
if (x[2] == y[0] && x[0] == y[2])
return true;
if (x[0] == y[2] && x[2] == y[0])
return true;
}
return false;
}
public int GetHashCode(string obj)
{
// return 0 to force the Equals to fire (otherwise it won't...!)
return 0;
}
#endregion
}
The sample program:
class Program
{
static void Main(string[] args)
{
List<string> MyList = new List<string>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
};
var distinct = MyList.Distinct(new CharComparer());
foreach (string s in distinct)
Console.WriteLine(s);
Console.ReadLine();
}
}
The result:
"A-B"
"C-D"
"C-E"
"D-E"
"F-G"
Very basic, but could be written better (but it's just working):
class Comparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return (x[0] == y[0] && x[2] == y[2]) || (x[0] == y[2] && x[2] == y[0]);
}
public int GetHashCode(string obj)
{
return 0;
}
}
var MyList = new List<String>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
}
.Distinct(new Comparer());
foreach (var s in MyList)
{
Console.WriteLine(s);
}
int checkID = 0;
while (checkID < MyList.Count)
{
string szCheckItem = MyList[checkID];
string []Pairs = szCheckItem.Split("-".ToCharArray());
string szInvertItem = Pairs[1] + "-" + Pairs[0];
int i=checkID+1;
while (i < MyList.Count)
{
if((MyList[i] == szCheckItem) || (MyList[i] == szInvertItem))
{
MyList.RemoveAt(i);
continue;
}
i++;
}
checkID++;
}