I'm trying to achieve the following scenarios:
Add 5 items of type T to a new Redis SET
Add 1 item of type T to an existing Redis SET
(i know SETADD doesn't care if the set is existing, but just listing my scenarios for reference)
I can see there is SetAddAsync(RedisKey, RedisValue value) and SetAddAsync(RedisKey, RedisValue[] values), but i'm not sure how to work with it (and which overload to use?)
When i've used StringSet, i simply serialize T to a byte[], then use that as the RedisValue param.
But not sure how to do it for sets.
This is what i have:
var items = values.Select(serializer.Serialize).ToArray();
await cache.SetAddAsync(key, items);
where serializer is a class which converts T to a byte[]
It is basically identical to how you would use StringSet. The only difference is that when setting a string, it only makes sense to set one value - but when adding to a set, you might want to add 1 or more elements at a time.
If you're adding one element, just use:
db.SetAdd[Async](key, serializedValue);
If you want to add a larger number of items to the set in one go, then first get the serialized items, for example:
var items = Array.ConvertAll(values, value => (RedisValue)serializer.Serialize(value));
or to tweak your existing code:
var items = values.Select(value => (RedisValue)serializer.Serialize(value)).ToArray();
The important difference here is that I expect your original code is ending up with a byte[][], where-as you need a RedisValue[]. The (RedisValue) cast in the above should fix that for you.
Then call:
db.SetAdd[Async](key, serializedValues);
This corresponds to the variadic form of SADD.
Related
I have a list of records that are immutable and I need to "change" a value in one of the records. It will of course mean creating a copy of the object with that value changed and then referencing the new records instead of the old one in the list.
I am wondering if there is some smart and neat way of doing it in C#. So far I have thought only of a straightforward way:
Get a copy of the record with changed value
Find the index of the record in the list
Assign the new reference
E.g:
var newRecord = oldRecord with { Field = newValue};
var index = list.IndexOf(oldRecord);
list[index] = newRecord;
Alternatively removing an item and putting a new one if it's not a list but an Enumerable e.g.
It looks like dotnet actually has a number of classes for these that are inside System.Collections.Immutable.
ImmutableList has a Replace function that will do exactly what I need.
It will return a new list as well, since it's the list itself that is immutable, so if that is not desired one must just implement an extension method that does about the same.
Here is my scenario. I am using WPF and making use of two way binding to show a collection objects received from a service call every 60 seconds. On the first call I create a collection of objects that will be displayed from the collection of service objects. On subsequent calls I need to compare the service collection to the existing collection and then do one of three things:
If the Item exists in both collections then update ALL of the values for the object in the Display collection with the values from the object in the service collection.
If the item Exists in the Service Collection and not the Display Collection then add it to the Display Collection.
If the Item exists in the Display collection and not the Service Collection then remove it from the Display collection.
I am looking for the best way to do this.
Adding & Removing
Is it smarter to do a Left Join here and return everything essentially unique to one side of the other and then add or remove that as appropriate?
Should I attempt to do a Union since Linq is supposed to merge the two and ignore the duplicates?
If so how does it decide uniqueness? Is it evaluating all the properties? Can I specify which collection to keep from and which to discard in merging?
Should I use Except to create a list of differences and somehow use that?
Should I create a new list to add and remove using Where / Not In logic?
Updating
Since the collections aren't dictionaries what is the best way to do the comparison:
list1.ForEach(x => list2[x.Id].SomeProperty = x.SomeProperty);
Is there some way of copying ALL the property values other than specifying each one of them similar to above? Can I perform some kind of shallow copy within Linq Without replacing the actual object that is there?
I don't want to just clear my list and re-add everything each time because the object is bound in the display and I have logic in the properties that is tracking deviations as values change.
You can use the except and intersect methods to accomplish most of what you are looking to do.
However, depending on the size of your objects this can be very resource intensive.
I would recommend the following.
var listIDsA = collectionA.Select(s => s.Id).Distinct().ToList();
var listIDsB = collecitonB.Select(s => s.Id).Distinct().ToList();
var idsToRemove = listIDsB.Select (s => !listIDsA.Contains(s.Id)).ToList();
var idsToUpdate = listIDsB.Select(s => listIDsA.Contains(s.Id)).ToList();
var idsToAdd = listIDsA.SelecT(s => !listIDsB.Contains(s.Id)).ToList();
Then using the three new collections you can add/remove/update the apporpriate records.
You can also use a hashedset instead of IEnumerables for better performance. This will require you to create an extension class to add that functionality. Here is a good explanation of how to do that (it's not complicated).
How to convert linq results to HashSet or HashedSet
If you do this, you will need to replace the .ToList() in the first two lines to .ToHasedSet()
For your comparison you need to overwrite equals and get hashcode
Object.GetHashCode Method
Then you can use List.Contains
List.Contains Method
If you can use HashSet then you will get better performance
Code not tested
ListDisplay.Remove(x => !ListSerice.Contains(x));
Foreash(ListItem li in ListDisplay)
{
ListItem lis = ListSerice.FirstOrDefault(x => x.Equals(li));
if (lis == null) continue;
// perform update
}
Foreach(ListItem li in ListSerice.Where(x => !ListDisplay.Contains(x))) ListDisplay.Add(li);
Here it is, I have a DatagridView bound to a list of objects - that's fine.
What I do is list 10 or so results at a time, where the results can be navigated with next and previous buttons that simply get the results out of the source and display them.
The issue is that I want the source to order by what ever column is clicked in the Datagrid view - not just the results being viewed at the time, all of the results.
I have finally started to get close. I can compare my source and even set an order by the name of the column being used to invoke the data value.
What I need to know is can I have it set the data type automatically:
AppSettings.Instance.SearchResults.Sort(
new Comparison<SearchResult>(
(x, y) =>
((Int32) x.GetType().GetProperty("Links").GetValue(x, null)).CompareTo(
((Int32) y.GetType().GetProperty("Links").GetValue(y, null)))));
Here I have set the property to Links, which is an Int. I cannot make use of CompareTo without converting the value even though it knows its an Int32. Some values I compare will be of the type DateTime so I need to know if its possible to compare and get the data type dynamically as I'm so close now.
Summary - I would like to automatically identify the data type of dynamically invoked property values.
I'm not sure if that's the best 'way forward', to do you what you wanna do.
if applicable I'd rather use IComparable<T> instead e.g.
int BetterComparison<T>(T x, T y) where T : IComparable<T>
{
return x.CompareTo(y);
}
And call it like:
BetterComparison(10, 20);
BetterComparison(DateTime.Now, DateTime.Now.AddDays(1));
If you'd still want to resort to reflection for some reason (or have things more generic in nature - or not defined by any interface like that)...
You should reflect on the CompareTo method for the specific type - and then just invoke with values you have. E.g. something like this (within your method):
// x.GetType() and y.GetType() should be interchangable by the nature of it
var compareMethod = x.GetType().GetMethod("CompareTo", new[] { y.GetType() });
return (int)compareMethod.Invoke(x, new object[]{y});
That allows you to call it like this:
var ret1 = Comparison(10, 20);
var ret2 = Comparison(DateTime.Now, DateTime.Now.AddDays(1));
Why does this code result in both shipment.Items.Count and combinedShipment.Items.Count equal to zero?
private static InboundShipment CombineLikeIsbns(InboundShipment shipment)
{
// shipment.Items has a count of 3
var distinctIsbns = shipment.Items.Select(i => i.ISBN).Distinct().ToList();
var combinedShipment = shipment;
combinedShipment.Items = new List<InboundShipmentItem>();
// Now both combinedShipment and shipment have an empty List in the .Items property
...
return combinedShipment;
}
[EDIT]
And what can I do to avoid having shipment.Items set to new List when I set combinedShipment.Items to the same?
This statement:
var combinedShipment = shipment;
copies the value of shipment into combinedShipment. Assuming InboundShipment is a class, the value of shipment is a reference - not an object itself.
So now we have two variables which both refer to the same object. It doesn't matter which variable you use to make a change to the object - the change will be visible via both variables.
If you want to create a new "copy" of the original object, you'll have to do that explicitly. It's hard to know exactly what you'd need to do here, as you haven't given us much information about the InboundShipment type.
See my article on value types and reference types for more details. Note that this is a vital part of C# and .NET, and you should become confident on it before going further - advanced topics such as LINQ (with its lambda expressions, deferred execution etc) will be hard to understand until you've got a good handle on the basics.
The line
var combinedShipment = shipment;
sets the combinedShipment reference to point at the same instance as shipment. So when you clear the items on combinedShipment, it clears them for that one single instance.
The assignment var combinedShipment = shipment; causes both combinedShipment and shipment to refer to exactly the same object. It's a little bit like sticking two different labels on the same cardboard box.
So the following line where you take all the items out of the box labeled combinedShipment also causes the box labeled shipment to be emptied. Because they're just the same box with two different labels.
If you want to create a new shipment object that has different items, you'll need to start by doing exactly that: Create a new shipment object.
var combinedShipment = new InboundShipment();
The actual code might not be able to look exactly like that. Assuming you want some of combinedShipment's properties to be the same as shipment's, you'll have to manually make sure that happens. Depending on how InboundShipment is designed, that will require either passing the desired values into the constructor, setting the desired values via properties, or a mix of both.
Normally when I need to have a list of ints/strings/etc. I create a list like:
var list = new List<string>
And then I create a hashtable that contains all the strings, and I don't insert into the list unless it isn't in the hashtable i.e. to enforce unique items in the list.
Is there a datatype that can satisfy both of these requirements for me?
There is. Use HashSet:
var set = new HashSet<int>();
set.Add(4);
set.Add(4); // there is already such an element in the set, no new elements added
Keep in mind, though, that it does not guarantee you the order of elements.
Do you just mean HashSet<string> ?
All elements in a HashSet<T> are unique; the Add() method returns a bool to indicate if a new item was actually added, or whether it was a no-op.
Is there a datatype that can satisfy both of these requirements for me?
No. A hashtable will provide you a direct access to an element given its unique key, whereas in a list you don't need a key and you could definitely have duplicates.
You can use the HashSet<T> data type MSDN. Which will only allow you to have a single copy of each value.
If you are after a set of unique values only (and don't subsequently care about ordering) then you should look at a HashSet<T>
Technically, there is System.Collections.Specialized.OrderedDictionary. However, this is an old non-updated (non-generic) class and I would generally recommend avoiding it ;-)
Represents a collection of key/value pairs that are accessible by the key or index.
In practice I would create a minimal wrapper class that exposes the required operations. (I would likely use a HashSet<T> (for existence) and a List<T> (for ordering), although just a single List<T> is far than sufficient for a relatively small n in most cases -- remember Big-O is about limits.)
Happy coding.
HashSet<string> set = new HashSet<string>();
bool inserted = set.Add("Item");
bool insertedDuplicate = set.Add("Item");
inserted.Dump("Result1");
insertedDuplicate.Dump("Result2");
//Result
//Result1 = true
//Result2 = false
You can run this in LinqPad to see the functionality and how it works.