IEnumerable - Update objects inside foreach loop - c#

I have a really simple program that creates a bunch of objects and iterates through them to set each object's Priority property.
static void Main(string[] args)
{
foreach (var obj in ObjectCreator.CreateObjectsWithPriorities())
Console.WriteLine(String.Format("Object #{0} has priority {1}",
obj.Id, obj.Priority));
}
class ObjectCreator
{
public static IEnumerable<ObjectWithPriority> CreateObjectsWithPriorities()
{
var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });
ApplyPriorities(objs);
return objs;
}
static void ApplyPriorities(IEnumerable<ObjectWithPriority> objs)
{
foreach (var obj in objs)
{
obj.Priority = obj.Id * 10;
Console.WriteLine(String.Format("Set priority of object #{0} to {1}", obj.Id, obj.Priority));
}
}
}
class ObjectWithPriority
{
public int Id { get; set; }
public int Priority { get; set; }
}
I'm expecting the IEnumerable in the Main method to contain objects with modified priorities. However, all of them have the default value 0.
Here is the log:
Set priority of object #1 to 10
Set priority of object #2 to 20
Set priority of object #3 to 30
Object #1 has priority 0
Object #2 has priority 0
Object #3 has priority 0
What is the reason for suche behavior and what should I change here in order to get my priorities working?

When you do this:
var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });
You're simply creating a lazily evaluated iterator, this doesn't allocate an array/list to store the ObjectWithPriorty you project. Each time you enumerate the iterator, it will iterate the values again and project an ObjectWithPriority for each iteration, but will discard them.
What you want to do is materialize the query before you pass it, so later you'll actually modifying the already allocated list/array. This can be achieved using Enumerable.ToList or Enumerable.ToArray:
public static IEnumerable<ObjectWithPriority> CreateObjectsWithPriorities()
{
var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i })
.ToList();
ApplyPriorities(objs);
return objs;
}
You could additional use Enumerable.Range instead of allocating a fixed size array, which will lazily project the numbers as requested:
var objs = Enumerable.Range(1, 3).Select(i => new ObjectWithPriority { Id = i })
.ToList();

To better understand what is happening in your program, you should think of this expression
var objs = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i });
as a query, not as a sequence/list/collection of objects.
But it is obvious from your code that in this particular program you don't need a query. You need a collection that has a finite number of objects and that returns the same objects every time you loop through it using foreach.
So a decent thing to do would be to use ICollection<ObjectWithPriority> instead of IEnumerable<ObjectWithPriority>. This would better represent the program's logic and help avoid some mistakes/misinterpretations like the one you stumbled upon.
The code could be modified as follows:
public static ICollection<ObjectWithPriority> CreateObjectsWithPriorities()
{
IEnumerable<ObjectWithPriority> queryThatProducesObjectsWithPriorities = new[] { 1, 2, 3 }.Select(i => new ObjectWithPriority() { Id = i }); // just for clarification
ICollection<ObjectWithPriority> objectsWithPriorities = queryThatProducesObjectsWithPriorities.ToList();
ApplyPriorities(objectsWithPriorities);
return objectsWithPriorities;
}
static void ApplyPriorities(ICollection<ObjectWithPriority> objs)
{
foreach (var obj in objs)
{
obj.Priority = obj.Id * 10;
Console.WriteLine(String.Format("Set priority of object #{0} to {1}", obj.Id, obj.Priority));
}
}

In Addition to the Answer of Yuval Itzchakov:
If you want to lazy-load the priority of your objects you could:
Define your ApplyPriorities() Method just for one object and use it in the select-Method OR add a delegate to your ObjectWithPriority class wich calculates the Priority like shown in the code below:
class ObjectWithPriority
{
public int Id { get; set; }
private int? priority;
public int Priority {
get
{
return (priority.HasValue ? priority.Value : (priority = PriorityProvider(this)).Value);
}
set { priority = value; }
}
Func<ObjectWithPriority, int> PriorityProvider { get; set; }
public ObjectWithPriority(Func<ObjectWithPriority, int> priorityProvider = null)
{
PriorityProvider = priorityProvider ?? (obj => 10 * obj.Id);
}
}

Related

How to find an element in a List<T> where name is the same and its time is the same or at least similar?

I am currently working on a project which uses two methods, one method returns the most accurate player list on a server with the duration the player was on the server, and the second method utilizes a different method which returns a player list with less accuracy and no time, but with an additional value which I need that other method doesn't have. To put it in simple terms:
Method 1:
List<PlayerObjectMethod1> playerListMethod1 = GetPlayersFromServerMethod1(serverIp, serverPort);
The class method 1:
public string Name { get; set; }
public float Duration { get; set; }
Method 2:
List<PlayerObjectMethod2> playersFromMethod2 = new List<PlayerObjectMethod2>();
The class method 1:
public string Name { get; set; }
public string SpecialValue { get; set; }
public string CustomDuration { get; set; }
Now as you can see the method 2 doesn't officially return duration, however this method is running every 15 seconds, so in theory, I could attach 15 seconds to each player every time it runs.
More background:
The parent method runs on a timer every 15seconds. There are 5 servers in total for one server (time in between specific server gets scanned) is around 18 seconds, as such each player on each call can be 18 seconds. I need to get an accurate player for that specific value. Two comparisons I want to do:
If a players name is not 123, only compare the name to get a specific value.
if(playerListMethod1[i].Name != "123") {
var index = playersFromMethod2.FindIndex(x => x==playerListMethod1[i].Name)
playersFromMethod2[index].IsOnline = True;
playersFromMethod2[index].Duration = playerListMethod1[i].Duration;
}
And now if it is 123 I need to find it by name and duration. However, the issue I have is how to upkeep that second list and add 15 seconds to all the players with name 123. As before I would use a list to store old player list value and just clear it and AddRange of the new one.
Example:
serverNotfPlayerListOld[server.Name].Clear();
serverNotfPlayerListOld[server.Name].AddRange(playersFromMethod2);
So I basically need an idea on how to do this, would I first fill the method2 with players, then check non 123 players, then check 123 players, and then add 15 seconds to the 123 players and at some point the list would get accurate?
Edit:
As mentioned before there are two different methods (two different sources) one gives name and duration, the other name and player id. As such, I need to somehow merge that data together. To do that I thought I could add my own duration for the second method because it's being run every 45 seconds. The current new code I have:
Example of the addition solution
class Program
{
static void Main()
{
HashSet<A> a = new HashSet<A>()
{
// add random values
new A { Id = "josh", Value = 60, },
new A { Id = "tom", Value = 60, },
new A { Id = "koven", Value = 120, },
new A { Id = "123", Value = 240, },
};
HashSet<A> b = new HashSet<A>()
{
// add random values (some with Id's from a)
new A { Id = "tom", Value = 10, },
new A { Id = "4123", Value = 10, },
new A { Id = "koven", Value = 65, },
new A { Id = "5552", Value = 60, },
new A { Id = "123", Value = 45, },
};
IEnumerable<A> c = IdJoin(a, b);
int i = 0;
foreach (A element in c)
{
Console.WriteLine($"{element.Id}: {element.Value}");
i++;
}
Console.WriteLine($"Count: {i}");
Console.WriteLine("Press [enter] to continue...");
Console.ReadLine();
}
public static IEnumerable<A> IdJoin(IEnumerable<A> a, IEnumerable<A> b)
{
Dictionary<string, A> dictionary = a.ToDictionary(i => i.Id);
foreach (A element in b)
{
if (dictionary.TryGetValue(element.Id, out A sameId))
{
if (element.Id == "123")
{
sameId.Value += element.Value;
}
else
{
sameId.Value += 45;
}
}
else {
dictionary.Add(element.Id, element);
}
}
return dictionary.Values;
}
}
public class A
{
public string Id;
public float Value;
}
Issue with this is that if it reads by only name it will bug out as multiple players can have 123. Which is why I need comparison method which gets by name and duration (of few minutes differences) in those two lists and I need help with that. Another example:
Two 123 players join the game. One list has values [name:123, duration:240],[name:123, duration:60] the other has [name:123, player:7548, customDuration: 225], [name:123, player:7555, customDuration: 90]
I need to get which player is which.
Presuming Id and Value combination makes unique value:
class Program
{
static List<A> firstList;
static List<A> secondList;
static List<A> resultList;
static void Main(string[] args)
{
// Fill firstList, secondList with data <your server methodes>
resultList = new List<A>();
foreach (var item in firstList)
{
var match = secondList.Find(a => a.Equals(item));
if (match != null)
{
if (item.Id == "123")
{
item.Value += match.Value;
}
else
{
item.Value += 45;
}
}
resultList.Add(item);
}
resultList.AddRange(secondList.Except(firstList));
}
}
public class A
{
public string Id;
public float Value;
public override bool Equals(Object obj)
{
if ((obj == null) || !GetType().Equals(obj.GetType()))
{
return false;
}
else
{
var a = (A)obj;
return (Id == a.Id) && (Value == a.Value);
}
}
}

Compare Object vs Dictionary<int,List<int>> considering performance c#

using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp5
{
class Validator
{
static void Main()
{
var metaValues = new List<Meta>
{
new Meta(4, 15, true),
new Meta(5, 20, false)
};
var requestDict = new Dictionary<int, List<int>>
{
{4, new List<int>{15,20} },// error not exist
{5, new List<int>{25} }, // error its false
{6, new List<int>{30} } // error not exist
};
var matchedIds = new List<int>();
if (metaValues.Any())
{
foreach (var ob in metaValues)
{
if (requestDict.ContainsKey(ob.Id))
{
matchedIds.Add(ob.Id);
var valuesDict = requestDict[ob.Id];
//here i cant get all the values and its Active of meta.Id
}
}
}
foreach (var key in requestDict.Keys)
{
if (!matchedIds.Contains(key))
Console.WriteLine("Invalid");
}
}
}
public class Meta
{
public int Id { get; private set; }
public int Value { get; private set; }
public bool IsActive { get; private set; }
public Meta(int id, int value, bool isActive)
{
Id = id;
Value = value;
IsActive = isActive;
}
}
}
iterating dictionary with object causing performance issue since everytime dictionary key has to be iterated in an list of object so i am trying to take object and lookup in dictionary on below condition
Invalid when meta.Id does not exist in dictionary key
Invalid when one of the meta.Value does not exist in dictionary values List
Inactive when meta.Id and meta.value match with dictionary but meta.isactive is false
I probably shouldn't bother answering since:
The code is quite messy
It does not compile
The question is very unclear
However, for some reason I feel like I understand a little what you're trying to do and wanted to provide some help.
First, let's NOT name a class with the same name as a built-in type (System.Object). Perhaps Item is generic enough? Also, you appear to instantiate instances of this class by calling a constructor that doesn't exist, so let's add that constructor as well:
public class Item
{
public int Id { get; }
public int Value { get; }
public bool IsActive { get; }
public Item(int id, int value, bool isActive)
{
Id = id;
Value = value;
IsActive = isActive;
}
}
Now we can create our list of Item objects by calling the constructor:
var items = new List<Item>
{
new Item(4, 15, true),
new Item(5, 20, false)
};
It also appears that you're creating a dictionary that contains a Key of type int that maps to Item.Id, and a Value of type List<int> that sort-of maps to Item.Value (though Item.Value is a single int). A problem in the code you posted is that you're trying to add two items with the same Key value of 4, which is not legal for a Dictionary - all the keys must be unique. To fix this, I'm using unique keys:
var requests = new Dictionary<int, List<int>>
{
{4, new List<int> {15}},
{5, new List<int> {20}},
{6, new List<int> {25}},
{7, new List<int> {30}}
};
Next it appears that you're trying to create a List<int> of numbers representing the Item.Id values that exist as dictionary keys. This can be done with a System.Linq extension method:
var matchedIds = items
.Where(item => requests.ContainsKey(item.Id))
.ToList();
And finally, it's not exactly clear what you want to do with this list, but it appears you want to do something if either an Item.Id does not exist in the dictionary, or the Item.Id exists but the Item.Value is not in the list, or the item does exist, but the Item.IsActive value is false, or some other combination of these properties.
Here's how to get those items:
var matchedIds = items
.Where(item => requests.ContainsKey(item.Id))
.ToList();
var matchedIdsAndValues = matchedIds
.Where(item => requests[item.Id].Contains(item.Value))
.ToList();
var matchedIdsMissingValue = matchedIds
.Where(item => !requests[item.Id].Contains(item.Value))
.ToList();
var unmatchedIds = items
.Where(item => !requests.ContainsKey(item.Id))
.ToList();
var matchedIdAndValueButNotActive = matchedIdsAndValues
.Where(item => !item.IsActive)
.ToList();
Hope this helps!

How would I find the value of an unspecified variable in other objects of the same class? C#

I want this method to work with any variable - i.e., passing a "Price" value to the method then getting the total price of all items.
private int GetTotalValue(int stat){
int total = 0;
foreach(Item i in Vendor.items){
totalStat += i.stat;
}
return total;
}
However, it has no way of knowing the name of the variable that I passed as the parameter, and thus no way of accessing it in other objects.
How would I tell the method what variable I'm passing, instead of just the value of it?
If you always want the sum of some property value you could encapsulate that logic into a method, e.g. GetVendorItemSum:
internal class Program
{
private static void Main(string[] args)
{
var items = new[] {
new Item {Price = 1},
new Item {Price = 2}
};
var vendor = new Vendor {Items = items};
var vendorSum = GetVendorItemsSum(vendor, x => x.Price);
}
private static int GetVendorItemsSum(Vendor vendor, Func<Item, int> func)
{
return vendor.Items.Sum(func);
}
}
public class Vendor
{
public IEnumerable<Item> Items;
}
public class Item
{
public int Price { get; set; }
}

EF 6 'Bulk' insert with FK relationed models

I am trying to bulk insert using EF (model first) on two tables that have a FK relationship (one to many). The code below properly inserts all of the Challenge entries but only inserts the X amount Shortages one time. My expectation... I have 10 shortages with 2 challenges. I should receive 2 challenge entries and 20 shortage entries. I am only seeing 10 challenge entries for the first shortage inserted. (the code below is simplified)
//class for cloning Shortage collection
public class ShortageCollection : Collection<Shortage>
{
public ShortageCollection(IList<Shortage> source) : base(source) { }
public ShortageCollection() { }
public ShortageCollection Clone()
{
return Clone(this);
}
public static ShortageCollection Clone(ShortageCollection shortage)
{
var res = new ShortageCollection();
foreach (var s in shortage)
{
res.Add(s.Clone());
}
}
}
public class Shortage : StandardDB.Shortage
{
public Shortage Clone()
{
return new Shortage()
{
PART_NUMBER = this.PART_NUMBER,
Note = this.Note,
Qty = this.Qty,
ResponseMachine = this.ResponseMachine
};
}
}
public void CreateChallenge()
{
var JSONJobs = new JavaScriptSerializer().Deserialize<string[]>(Jobs);
var JSONParts = new JavaScriptSerializer().Deserialize<ChallengePartsList[]>(Parts);
using (ARTEntities art = new ARTEntities())
{
art.Configuration.AutoDetectChangesEnabled = false;
art.Configuration.ValidateOnSaveEnabled = false;
ShortageCollection sColl = new ShortageCollection();
foreach(var part in JSONParts)
{
Shortage s = new Shortage()
{
PART_NUMBER = part.Invid,
Note = Challenge,
Qty = part.Qty,
ResponseMachine = ResponseMachine
};
sColl.Add(s);
}
foreach (var job in JSONJobs) {
Challenge c = new Challenge()
{
InitiatorORG = Org,
TypeID = TypeID,
DISCRETE_JOB = job,
InitiatorPERSON_ID = InitiatorPersonID,
InitiatedDate = datenow,
Challenge1 = Challenge,
ChampionGroupID = ChampionGroupID,
StatusID = StatusID,
InitiatorGroupID = InitiatorGroupID,
DivisionID = DivisionID,
Shortages = sColl.Clone()
};
art.Challenges.Add(c);
}
art.SaveChanges();
}
}
you are only creating 10 Shortages in memory. Your Clone method is a shallow clone, and doesn't go through and clone every object. Therefore you have 2 lists with 10 identical items (each pair of items point to exactly same memory reference).
What you need to do is DeepClone which is something like the following:
public static ShortageCollection Clone(ShortageCollection shortage)
{
var res = new ShortageCollection();
foreach(var s in shortage) {
res.Add( s.Clone() );
}
return res;
}
And in your shortage class:
public class Shortage
{
public Shortage Clone()
{
return new Shortage()
{
SomeProp = this.SomeProp,
SomeOtherProp = this.SomeOtherProp
}
}
}
Be aware that if inside shortage any of those objects point to another entity, each pair will point to the same entity.
Search for DeepClone for more info

C#: Distinction with containing List

I have a List of instances of a class. Each instance contains a IList-property and a object of a custom class. Now I want to drop all distict instances in this List, where the elements of the containing IList-Property and the Multi-Property of the MyMulti-Class are equal. I want to remain only the one element in the List, where the "First"-property of the "someData"-Property is the lowest.
My suggestion was, to group the elements of the List by the containing IList-Property and the "Multi"-property of the "SomeData"-property and then sort it by the "First"-property of the "SomeData"-Property and select the first Element of each group.
I need it as performant as possible. Anyone any ideas?
Example:
class myClass
{
public IList<String> SomeStrings { get; set; }
public MyMulti SomeData { get; set; }
public myClass(MyMulti data, params string[] strings)
{
SomeData = data;
SomeStrings = strings;
}
}
class MyMulti
{
public int First { get; set; }
public int Second { get; set; }
public int Multi
{
get
{
return First * Second;
}
}
public MyMulti(int first, int second)
{
First = first;
Second = second;
}
}
var mc1 = new myClass(new MyMulti(6, 4), "0", "1");
var mc2 = new myClass(new MyMulti(3, 8), "0", "1");
var mc3 = new myClass(new MyMulti(7, 3), "0", "1");
var mc4 = new myClass(new MyMulti(2, 35), "0", "2");
var mc5 = new myClass(new MyMulti(5, 4), "1", "1");
var mc6 = new myClass(new MyMulti(7, 10), "0", "2");
IList<myClass> aList = new List<myClass>(){mc1, mc2, mc3, mc4, mc5, mc6};
var query = aList.GroupBy(x =>
new
{
x.SomeData.Multi,
x.SomeStrings
// don't work cause this will compare the lists
// not the containing elements
})
.Select(x=>
x.OrderBy(y=>
y.SomeData.First)
.First());
Result should be: mc1 and mc2 grouped and then drop mc1, mc3 own group, mc4 and mc6 grouped and then mc6 droped, mc5 own group --> mc2, mc3, mc4, mc5 should remain
EDIT:
If I first sort the List by the MyClass.SomeData.First-property and then use the List.Distinct()-method and a custom IEqalityComparer implementiation I get, what I'm searching for. But is this the most performant way to do this?
public class myClassEqualityComparer : IEqualityComparer<myClass>
{
public bool Equals(myClass x, myClass y)
{
if (!x.SomeData.Multi.Equals(y.SomeData.Multi))
return false;
else
if (x.SomeStrings.AsEnumerable()
.SequenceEqual(y.SomeStrings.AsEnumerable()))
return true;
return false;
}
public int GetHashCode(myClass obj)
{
int hCode = obj.SomeData.Multi;
foreach (var item in obj.SomeStrings)
{
hCode = hCode ^ item.GetHashCode();
}
return hCode.GetHashCode();
}
}
why dont you try Distinct() method might do your wrok
List.Distinct();
you GetHashCode function may slow thing down.
Hash code is used to balance hash table and for the best performance, a hash function must generate a random distribution for all input. You should try:
public int GetHashCode(myClass obj)
{
return obj.SomeData.First ^ obj.SomeData.Second;
}
You may test performance using System.Diagnostics.Stopwatch and loop 100000 to 1000000 times.

Categories

Resources