Update a Dictionary value using Linq - linq

I will call AnalyseLinqUpdate()
I think code itself clear..
I have to find behavior for each dictionary value and replace the value with the behavior I get from the method 'GiveBehavior'
void AnalyseLinqUpdate()
{
Dictionary<string, string> rawCollection = new Dictionary<string, string>();
rawCollection.Add("PT-1", "PTC-1");
rawCollection.Add("PT-2", "PTC-1");
rawCollection.Add("PT-3", "PTC-2");
rawCollection.Add("PT-4", "PTC-2");
rawCollection.Add("PT-5", "PTC-3");
rawCollection.Add("PT-6", "PTC-3");
//update here
// call GiveBehavior("PTC-1");
//returns a string that needs to be updated in place of "PTC-1"
}
string GiveBehavior(string ptc)
{
StringComparison ignoreCase = StringComparison.OrdinalIgnoreCase;
ptc = ptc.Trim();
if (ptc.Equals("PTC-1", ignoreCase))
{
return "PTB-1";
}
else if (ptc.Equals("PTC-1", ignoreCase))
{
return "PTB-2";
}
else
{
return "PTB-3";
}
}
Currently I have done like:
List<string> keys = rawCollection.Keys.ToList();
foreach (string key in keys)
{
string behavior = GiveBehavior(rawCollection[key]);
rawCollection[key] = behavior;
}
This is how I update the dictionary..
Is there anyway tat can be done via LINQ...

You could try the following:
List<string> keys = rawCollection.Keys.ToList();
keys.ForEach(key => { rawCollection[key] = GiveBehavior(rawCollection[key]); });

That should do it
rawCollection = rawCollection.ToDictionary(item => item.Key, item => GiveBehavior(item.Key));

Related

Multiple sorting on single index of Algolia

Can we put multiple sorting/custom ranking on single index of Algolia without creating multiple replica indexes for each sorting.
Lets understand with example suppose my application is SalesManagement and I create 3 replicas MobileSale,LaptopSale and PhoneSale and then in each one I put custom sorting to type = 'mobile',type = 'laptop' and type = 'phone' respectively.
Instead of doing above I just want to create one index and put sorting on compile time by sending different settings by using IndexSetting in my code so I don't need to create multiple indexes
Using ranking property of IndexSetting it worked for me
private readonly SearchClient algoliaClient;
private readonly SearchIndex feedIndex;
public AlgoliaService( )
{
algoliaClient = new SearchClient("app-id", "api-key");
SearchIndex = algoliaClient.InitIndex(_defaultSettings.Algolia.FeedIndex);
algoliaClient.InitIndex("user-index-name");
}
public async Task SetCustomSortingAttributes(string sortBy)
{
if (sortBy != null)
{
List<string> sortingAttributesList = new List<string>();
StringBuilder sortAttribute = new StringBuilder();
if (sortBy == "most_viewed")
{
sortAttribute.Append("desc(most_viewed)");
}
else if (sortBy == "entityUniqueViews" )
{
sortAttribute.Append("desc(entityUniqueViews)");
}
else if (sortBy == "latest")
{
sortAttribute.Append("desc(latest)");
}
else if (sortBy == "price_asc")
{
sortAttribute.Append("asc(price)");
}
else
{
sortAttribute.Append("desc(price)");
}
sortingAttributesList.Add(sortAttribute.ToString());
IndexSettings settings = new IndexSettings
{
Ranking = sortingAttributesList
};
var setIndexSetting = await feedIndex.SetSettingsAsync(settings);
}
}

How to Clear an observable collection without using clear method

When i am trying to clear my observable collection it is crashing.Is there any
other way to clear it??
ObservableCollection<Group> _groupList = new ObservableCollection<Group>();
public ObservableCollection<Group> GroupList
{
get
{
return _groupList;
}
set
{
_groupList = value;
RaisePropertyChanged("GroupList");
}
}
You can simple make a new collection:
GroupList = new ObservableCollection<Group>();
There's a clear function
GroupList.Clear();
or
GroupList = new ObservableCollection<Group>();
Also try declare you list this way :
ObservableCollection<Group> _groupList;
public ObservableCollection<Group> GroupList
{
get { return _groupList; }
set { SetValue(ref _groupList, value); }
}

EmitMapper and List

It's the first time that I use EmitMapper.
I have a list of object ex: Customer and I would like to map this list in a ienumerable of CustomerDTO how can I do that?
Tnx
It's straightforward if you have a list and want to convert it to list of DTOs:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Customer, CustomerDTO>();
IEnumerable<CustomerDTO> dtos = listOfCustomer.Select(mapper.map);
The preblem is when the list is in another object, for example User and UserDTO:
class User {
public List<Customer> Customers { get; set; }
}
class UserDTO {
public IEnumerable<CustomerDTO> Customers { get; set; }
}
It seems that EmitMapper does not support conversion from List to Enumerable. A way to support it would be:
var customerMapper = ObjectMapperManager
.DefaultInstance.GetMapper<Customer, CustomerDTO>();
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<User, UserDTO>(
new DefaultMapConfig()
.ConvertUsing<List<Customer>, IEnumerable<CustomerDTO>>(
a => a.Select(customerMapper.Map))
);
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

Building an external list while filtering in LINQ

I have an array of input strings that contains either email addresses or account names in the form of domain\account. I would like to build a List of string that contains only email addresses. If an element in the input array is of the form domain\account, I will perform a lookup in the dictionary. If the key is found in the dictionary, that value is the email address. If not found, that won't get added to the result list. The code below will makes the above description clear:
private bool where(string input, Dictionary<string, string> dict)
{
if (input.Contains("#"))
{
return true;
}
else
{
try
{
string value = dict[input];
return true;
}
catch (KeyNotFoundException)
{
return false;
}
}
}
private string select(string input, Dictionary<string, string> dict)
{
if (input.Contains("#"))
{
return input;
}
else
{
try
{
string value = dict[input];
return value;
}
catch (KeyNotFoundException)
{
return null;
}
}
}
public void run()
{
Dictionary<string, string> dict = new Dictionary<string, string>()
{
{ "gmail\\nameless", "nameless#gmail.com"}
};
string[] s = { "anonymous#gmail.com", "gmail\\nameless", "gmail\\unknown" };
var q = s.Where(p => where(p, dict)).Select(p => select(p, dict));
List<string> resultList = q.ToList<string>();
}
While the above code works (hope I don't have any typo here), there are 2 problems that I do not like with the above:
The code in where() and select() seems to be redundant/repeating.
It takes 2 passes. The second pass converts from the query expression to List.
So I would like to add to the List resultList directly in the where() method. It seems like I should be able to do so. Here's the code:
private bool where(string input, Dictionary<string, string> dict, List<string> resultList)
{
if (input.Contains("#"))
{
resultList.Add(input); //note the difference from above
return true;
}
else
{
try
{
string value = dict[input];
resultList.Add(value); //note the difference from above
return true;
}
catch (KeyNotFoundException)
{
return false;
}
}
}
The my LINQ expression can be nicely in 1 single statement:
List<string> resultList = new List<string>();
s.Where(p => where(p, dict, resultList));
Or
var q = s.Where(p => where(p, dict, resultList)); //do nothing with q afterward
Which seems like perfect and legal C# LINQ. The result: sometime it works and sometime it doesn't. So why doesn't my code work reliably and how can I make it do so?
If you reverse the where and the select you can convert unknown domain accounts to null first, then just filter them out.
private string select(string input, Dictionary<string, string> dict)
{
if (input.Contains("#"))
{
return input;
}
else
{
if (dict.ContainsKey(input))
return dict[input];
}
return null;
}
var resultList = s
.Select(p => select(p, dict))
.Where(p => p != null)
.ToList()
This takes care of your duplicate code.
It takes 2 passes. The second pass converts from the query expression to List.
Actually this is only one pass as LINQ is lazy evaluated. This is why your last statements only work sometimes. The filter is only applied and your list generated if the LINQ query is evaluated. Otherwise the Where statement is never run.
It sounds like what you want is an iterator. By making your own iterator you can filter the list and produce output at the same time.
public static IEnumerable EmailAddresses(IEnumerable<string> inputList,
Dictionary<string, string> dict)
{
foreach (string input in inputList)
{
string dictValue;
if (input.Contains("#"))
yield return input;
else if (TryGetValue(input, out dictValue)
yield return dictValue;
// else do nothing
}
}
List<string> resultList = EmailAddresses(s, dict).ToList();
You don't generally want to have side-effects on an unrelated object like your list. It makes it difficult to understand, debug, and refactor. I wouldn't worry about optimizing the query until you know it's not performing well.
So, what's wrong with your original expression? You don't need both the select and the where. You only need the Where() call. This will return a list of email addresses, which you can stick into a HashSet. The HashSet will provide the uniqueness you seem to desire. This will add execution time, so if you don't need it, don't use it.
You should only really need something like:
var s = new[] {"me#me.com", "me_not_at_me.com", "not_me"};
var emailAddrs = s.Where( a => a.Contains("#")); // This is a bad email address validator; find a better one.
var uniqueAddrs = new HashSet<string>(emailAddrs);
(Note, I've not dealt with HashSet, so the constructor might not take an Enumerable. This would be an exercise for the reader.)
Here is one way you could approach it with LINQ. It groups the values by whether or not they are email addresses, resulting in 2 groups of strings. If a group is the email address group, we select directly from it, otherwise we look up the emails and select from those:
public static IEnumerable<string> SelectEmails(
this IEnumerable<string> values,
IDictionary<string, string> accountEmails)
{
return
from value in values
group value by value.Contains("#") into valueGroup
from email in (valueGroup.Key ? valueGroup : GetEmails(valueGroup, accountEmails))
select email;
}
private static IEnumerable<string> GetEmails(
IEnumerable<string> accounts,
IDictionary<string, string> accountEmails)
{
return
from account in accounts
where accountEmails.ContainsKey(account)
select accountEmails[account];
}
You would use it like this:
var values = new string[] { ... };
var accountEmails = new Dictionary<string, string> { ... };
var emails = values.SelectEmails(accountEmails).ToList();
Of course, the most straightforward way to implement this extension method would be #gabe's approach.

Hierarchical structure iteration and LINQ

Assume that we have class
public class RMenuItem
{
public List<RMenuItem> ChildrenItems { get; }
public decimal OperationID { get; }
public string Name { get; }
}
as you can see - each menuitem could have children items - as usual in menu.
My task is to iterate through each items of this list and apply some action to it. Classical decision is to write recursive iteration. But I'm interesting if LINQ could make my task easier? For example, I suppose that we can write query that can get flat list of objects, which i can iterate simply with foreach. But my attempts in this way weren't successful yet.
So any help appreciated!
It's possible:
public void PrintAllNames(RMenuItem rootItem)
{
Action<RMenuItem> print = null;
print = m =>
{
Console.WriteLine(m.Name);
m.ChildrenItems.ForEach(print);
};
print(rootItem);
}
Notice how it's necessary to declare print so that print can use itself. This is directly comparable to a recursive method, which I'd rather use:
public void PrintAllNames(RMenuItem rootItem)
{
Console.WriteLine(rootItem.Name);
rootItem.ChildrenItems.ForEach(PrintAllNames);
}
(although for a more complex situation, maybe the functional solution would make the most sense)
I suggest 2 ways of achieving this. You can opt with an utility method to get all the items or you can implement the Visitor Pattern, though it implies changing the RMenuItem class.
Utility method:
static IEnumerable<RMenuItem> GetAllMenuItems(IList<RMenuItem> items)
{
if (items == null)
throw new ArgumentNullException("items");
Queue<RMenuItem> queue = new Queue<RMenuItem>(items);
while (queue.Count > 0)
{
var item = queue.Dequeue();
if (item.ChildrenItems != null)
{
foreach (var child in item.ChildrenItems)
{
queue.Enqueue(child);
}
}
yield return item;
}
}
I prefer an imperative way to a recursive because we can use iterator blocks.
Visitor Pattern:
public interface IRMenuItemVisitor
{
void Visit(RMenuItem item);
}
public class PrintRMenuItemVisitor : IRMenuItemVisitor
{
public void Visit(RMenuItem item)
{
Console.WriteLine(item);
}
}
public interface IRMenuItem
{
void Accept(IRMenuItemVisitor visitor);
}
public class RMenuItem : IRMenuItem
{
// ...
public void Accept(IRMenuItemVisitor visitor)
{
visitor.Visit(this);
if (ChildrenItems != null)
{
foreach (var item in ChildrenItems)
{
item.Accept(visitor);
}
}
}
}
Usage:
RMenuItem m1 = new RMenuItem
{
Name = "M1",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M11" },
new RMenuItem {
Name = "M12",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M121" },
new RMenuItem { Name = "M122" }
}
}
}
};
RMenuItem m2 = new RMenuItem
{
Name = "M2",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M21" },
new RMenuItem { Name = "M22" },
new RMenuItem { Name = "M23" }
}
};
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in GetAllMenuItems(menus))
{
Console.WriteLine(menu);
}
// or
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in menus)
{
menu.Accept(new PrintRMenuItemVisitor());
}
You could difine a Flatten method in your class (or as an extension if you prefer) like this
public IEnumerable<RMenuItem> Flatten()
{
foreach (var item in ChildrenItems)
{
yield return item;
}
return ChildrenItems.SelectMany(item => item.Flatten());
}
then doing somthing with each elements will be as simple as
RMenuItem rootItem ;
// do somthing with the root item
foreach (var item in rootItem.Flatten())
{
// do somthing
}
Indeed you can do that using LINQ, SelectMany flats out the list, just some example
menuItemsList.SelectMany(x => x.ChildrenItems).Where(c => c.someChildProperty);
Thanks
Edit:
In response to the comments, I was just giving an example of SelectMany previously. Thanks for pointing out.
menuItemsList.SelectMany(x => x.ChildrenItems.Select(p => p)).Where(c => c.someChildProperty);
OR something like this
menuItemsList.SelectMany(x => x.ChildrenItems).Select(p => p).Where(c => c.someChildProperty);
Edit2
Ahh .. now I understood what you want ..
We can just slightly modify my above query to do what you want
menuItemsList
.SelectMany(x => { //do something with x like printing it
x.ChildrenItems
})
.Select(p => { // do something with p like printing it
p
});
Basically you can do what you want the element inside the {}
Thanks

Resources