LINQ Update one list from another - c#

I'm trying to find an elegant way of updating values in a ConcurrentDictionary. I've created a quick example of what I'm trying to achieve below:
ConcurrentDictionary<int, MyDataClass> dataLookup = new ConcurrentDictionary<int, MyDataClass>();
// Initialise the example dataLookup with some dummy data
new List<MyDataClass>
{
new MyDataClass { Id = 1, ValueProperty = 0 },
new MyDataClass { Id = 2, ValueProperty = 0 },
new MyDataClass { Id = 3, ValueProperty = 0 },
new MyDataClass { Id = 4, ValueProperty = 0 },
new MyDataClass { Id = 5, ValueProperty = 0 }
}.ForEach(myClass => dataLookup.TryAdd (myClass.Id, myClass));
// incoming results that need to be fed into the above dataLookup
List<MyDataClass> newDataReceived = new List<MyDataClass>
{
new MyDataClass { Id = 1, ValueProperty = 111 },
new MyDataClass { Id = 3, ValueProperty = 222 },
new MyDataClass { Id = 5, ValueProperty = 333 }
};
So in the above example I want to set the ValueProperty in the dataLookup ConcurrentDictionary with the Id of 1, 3 & 5 to 111, 222 and 333 respectively. I can change the newDataReceived object into anything I want but I'm pretty much stuck with the dataLookup as a ConcurrentDictionary.
At the moment I'm iterating through the list but I'm looking for some suggestions on using LINQ to make this task more efficient.

If it's truly just updates that are coming in, you could just use another ForEach:
newDataReceived.ForEach(x => dataLookup[x.Id].ValueProperty = x.ValueProperty);
Personally I would just express this with a simple foreach loop though:
foreach(var update in newDataReceived)
dataLookup[update.Id].ValueProperty = update.ValueProperty;
Note that above is missing a check whether the item is actually contained in the concurrent dictionary - if that is not guaranteed (it's not an update) you would have to add this check.

Just tried to update in Join() and surprised it worked.
You obviously have to implement your own IEqualityComparer in order to compare only required members such as ID, key or similar.
private static void SaveProcessedFile(IEnumerable<HashedRow> processedRows, IEnumerable<HashedRow> justProcessedRows)
{
var comparer = new HashedRowEqualityComparerOrderLine();
var updated = justProcessedRows.Join(processedRows, n => n, o => o, (n, o) => { o = n; return n; }, comparer); // n - new, o - old
var inserted = justProcessedRows.Except(updated, comparer);
// To do something
}

Linq is for querying, not updating. If you were able to create a new dictionary it would be possible by using Linq's ToDictionary() method, but since you have to call a method to add you're more or less relegated to a foreach loop.
Also, Linq wouldn't make anything more efficient, it just makes the code look more natural.

Related

How to get item from IEnumerable collection using its index in C#? [duplicate]

This question already has answers here:
How to get the index of an element in an IEnumerable?
(12 answers)
Closed 4 years ago.
I have IEnumerable list of customerList and index. Now I want to get item of IEnumerable based on index.
For example, if index = 3, it should provide me 3rd item of the IEnumerable .
Please guide.
IEnumerable<Customer> customerList = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 915 },
new Customer { Name = "test8", Id = 986 },
new Customer { Name = "test9", Id = 988 },
new Customer { Name = "test4", Id = 997 },
new Customer { Name = "test5", Id = 920 },
};
int currentIndex = 3; //want to get object Name = "test8", Id = 986
For example, if index = 3, it should provide me 3rd item of the
IEnumerable
You know that indexes are zero based in .NET? However, you can use ElementAt:
Customer c = customerList.ElementAt(currentIndex); // 4th
Use ElementAtOrDefault to prevent an exception if there are not enough items, then you get null:
Customer c = customerList.ElementAtOrDefault(currentIndex); // 4th or null
These methods are optimized in a way that they use the IList<T> indexer. So in your example there is actually an Customer[] which implements that interface, hence it will use the indexer.
If the sequence does not implement IList<T> it will be enumerated to find the item at this index.
Don't use IEnumerable if you want index-based access. Use IList instead :
IList<Customer> customerList = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 915 },
new Customer { Name = "test8", Id = 986 },
new Customer { Name = "test9", Id = 988 },
new Customer { Name = "test4", Id = 997 },
new Customer { Name = "test5", Id = 920 },
};
With IEnumerable, the only option is to use the ElementAt() extension method.
IEnumerable<T> guarantees only that a collection can be enumerated. It makes no other promises about accessing elements. ElementAt allows access to a specific element by enumerating that collection one item at a time until the required element is reached. That can be expensive.
Luckily, ElementAt checks to see whether the argument is an IList and uses index-based access if possible.
That doesn't mean you should use IEnumerable<T> though. It will confuse maintainers (including you) that don't know what the actual instance behind the variable is. It will also cause performance issues if something that isn't an IList is ever assigned to that variable
This can be achieved using ElementAt method.
If your IEnumerable is not a materialized collection, but a generated sequence, calling ElementAt method multiple times will cause the sequence to be generated multiple times. This is usually not desired, as it unnecessarily consumes resources.
Some of the answers above suggest using IList instead of IEnumerable. Yes, definitely it'll make accessing by index less cumbersome, but using IList has a nasty side effect it makes the collection mutable. I'd use IReadOnlyList instead.
IReadOnlyList<Customer> customerList = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 915 },
new Customer { Name = "test8", Id = 986 },
new Customer { Name = "test9", Id = 988 },
new Customer { Name = "test4", Id = 997 },
new Customer { Name = "test5", Id = 920 },
};
int currentIndex = 3; //want to get object Name = "test8", Id = 986
var result = customerList[currentIndex];
customerList.Add(new Customer()); // doesn't compile
customerList[currentIndex] = new Customer(); // doesn't compile
IEnumerable is a 'streaming' data type, so think of it like a stream instead of an array.
var yourCustomer = customerList.Skip(2).First()
However if you wanted a more array like syntax IList may be a better abstraction for your use case.

array/list management questionon c#, total noob

i am a total noob when it comes to c#
i migrated to a new platform which uses c#, trying to migrate trading rules to new design.
i need to do this for algorithm
here is the question
i have keys array a,b,c,d
i have value set1 3,8,9,10
another value set2 77,89,100,76
these values are related to each other , (a) has values 3,77 and so on
what i need is , i need to filter with value set 2 , for example only values more than 80 then (probably create a new list with the remaining rows) , from the remaining list i need to get the keyname with highest set1 value
i tried it with this probably very bad way,
Array.Sort on one dimensional array value set1, take value[3]
- if this equals 3 then (if value set1 >80 valuefound else take value[2] and repeat
can you show me an easier way , please take my inexperience into account and include as more informatioan and code as possible
You should really start learning more about Linq. C# has very powerful functional-like features that you can do these things really easily with. It's actually really fun :)
Basically, this code does what you want with 3 lines.
var set1 = new[] {3, 8, 9, 10};
var set2 = new[] {77, 89, 100, 76};
var maxFromSet1 = set1
.Zip(set2, (fromSet1, fromSet2) => new {FromSet1 = fromSet1, FromSet2 = fromSet2}) //Match the sets to one another
.Where(zipped => zipped.FromSet2 > 80) // Filter by value
.Max(zipped => zipped.FromSet1); //Gets max
Another way of doing what you want would be to use a Dictionary which holds keys and values together instead of having them in different arrays.
Dictionary<string, int[]> dic = new Dictionary<string, int[]>()
{
{ "a", new[] { 3, 77 } },
{ "b", new[] { 8, 89 } },
{ "c", new[] { 9, 100 } },
{ "d", new[] { 10, 76 } }
};
Then using LINQ you can retrieve the key really easily
string key = dic.Where(x => x.Value[1] > 80) // Filter by second value
.OrderByDescending(x => x.Value[0]) // Order by first value
.First() // Get the max value
.Key; // Get the matching key

Extract Key Value Pairs from a named Collection MVC

I am pulling form values from a loosely bound razor form. The reason I am not using strongly bound model is that the payment value fields and categories are dynamic.
The form collection array reaching the controller is as below:
Payment {"1":"120","4":"23","6":"12","8":"120","9":"100"}
I need to split the array like (When PayCatId =1, Pay =120) (When PayCatId =4, Pay=23) etc..
string [] pa =collection["Payment"].Split(char.Parse(","));
string [] pc =collection.AllKeys["Payment"].Split(char.Parse(","));
Then I am trying to save to database using the logic below;
for (var i = 0; i < pa.Length; i++)
{
payment.Pay = Convert.ToDecimal(pa[i]);
payment.PayCatId = Convert.ToInt32(pa[i]); (added:how do i get this value from pair?)
payment.PayDate = DateTime.Now;
db.Payments.Add(payment);
db.SaveChanges();
}
Removed the Error bit as I have been enlightened that that approach is not applicable
I also want to know if this is the right and reliable approach to achieve this objective.
Just loop through two steps at a time instead of one
for (var i = 0; i < pa.Length; i+=2)
{
payment.Pay = Convert.ToDecimal(pa[i]);
payment.PayCatId = Convert.ToInt32(pa[i+1]);
payment.PayDate = DateTime.Now;
db.Payments.Add(payment);
db.SaveChanges();
}
You can use a combination of Split, Select and ToDictionary to do this, see the code:
var srt = "\"1\":\"120\",\"4\":\"23\",\"6\":\"12\",\"8\":\"120\",\"9\":\"100\"";
srt.Split(',')
.Select(x => x.Split(':'))
.ToDictionary(x => int.Parse(x[0].Replace("\"","")), x => int.Parse(x[1].Replace("\"","")))
/*
Output:
Dictionary<int, int>(5)
{
{ 1, 120 },
{ 4, 23 },
{ 6, 12 },
{ 8, 120 },
{ 9, 100 }
}
*/

Incorrectly making new collection from existing collections

I am still a beginner with C# so I am sure I am missing some fundamental concept here but I am struggling with this.
I am trying to make a new collection from two existing collections.
The first is a Dictionary<string, someModel[]>
The second is a Dictionary<string, string>
I am trying to find matches between the two dicts keys and if they match make a new myOtherModelwith the values from the two dicts, but if they don't match I still want to make new myOtherModel but with an empty string for the missing value then add all those new myOtherModel will be added to a list.
The new myModel object will be one of two scenarios
For example: Dict1.Keys = 1,2,3,4....100. Dict2.Keys = 5,9,27,55
myList.Add(new myModel = {1, "", someModel[]}) //did not find a match
myList.Add(new myModel = {5, dict2.MatchingValue, someModel[]}) // did find a match
So basically, compare two dictionaries, for each item in the larger dictionary, make a new myModel with the item's values (one of them will be empty). But if that item's key matches a key in the other dictionary, grab the second dictionary's value and slap that in the new myModel
I tried messing around with a Tuples but I wasn't able to manipulate them how I wanted to.
This is what I have so far, but instead of giving me 490 items (the count from dict1) I get the 44k (the amount of the two being multiplied together)
foreach (var pair in dict1)
{
foreach (var item in dict2)
{
if (item.Key == pair.Key)
{
var x = new myModel()
{
prop1 = item.Value,
prop2 = pair.Key,
prop3 = pair.Value
};
myListOfModels.add(x);
}
else
{
var x = new myModel()
{
prop1 = "",
prop2 = pair.Key,
prop3 = pair.Value
};
myListOfModels.add(x);
}
}
}
You're looping through the second collection each time you iterate through the first collection which is why you're seeing too many results. You can easily simplify your code with a simple bit of Linq...
foreach (var pair in dict1)
{
// Get the matched value. If there isn't one it should return the default value for a string.
var matchedValue = dict2.Where(x => x.Key == pair.Key).Select(x => x.Value).SingleOrDefault();
var x = new myModel()
{
prop1 = matchedValue,
prop2 = pair.Key,
prop3 = pair.Value
};
myListOfModels.add(x);
}

Auto-initializing C# lists

I am creating a new C# List (List<double>). Is there a way, other than to do a loop over the list, to initialize all the starting values to 0?
In addition to the functional solutions provided (using the static methods on the Enumerable class), you can pass an array of doubles in the constructor.
var tenDoubles = new List<double>(new double[10]);
This works because the default value of an double is already 0, and probably performs slightly better.
You can use the initializer:
var listInt = new List<int> {4, 5, 6, 7};
var listString = new List<string> {"string1", "hello", "world"};
var listCustomObjects = new List<Animal> {new Cat(), new Dog(), new Horse()};
So you could be using this:
var listInt = new List<double> {0.0, 0.0, 0.0, 0.0};
Otherwise, using the default constructor, the List will be empty.
Use this code:
Enumerable.Repeat(0d, 25).ToList();
new List<double>(new double[25]); //Array elements default to 0
One possibility is to use Enumerable.Range:
int capacity;
var list = Enumerable.Range(0, capacity).Select(i => 0d).ToList();
Another is:
int capacity;
var list = new List<double>(new double[capacity]);
A bit late, but maybe still of interest:
Using LINQ, try
var initializedList = new double[10].ToList()
...hopefully avoiding copying the list (that's up to LINQ now).
This should be a comment to Michael Meadows' answer, but I'm lacking reputation.
For more complex types:
List<Customer> listOfCustomers =
new List<Customer> {
{ Id = 1, Name="Dave", City="Sarasota" },
{ Id = 2, Name="John", City="Tampa" },
{ Id = 3, Name="Abe", City="Miami" }
};
from here: David Hayden's Blog

Categories

Resources