Changing List Value in C # Also Changes Your Copy Element - c#

I have a list of objects, and I'm passing them by parameter to a function. I want to edit it with some different values! because when I change an item from my list p does it change from the Pessons list, too? it's like accessing the same memory location, but I just want to replicate the list and alter it without causing changes to the other list.
public void GerarPorLider(List<Person> Persons)
{
List<Person> p = Persons;
p[0].Amount += 10;
}
The output that is happening when I execute this code
Persons[0].Amount = 17; p[0].Amount = 17;
The way I expected it
Persons[0].Amount = 7; p[0].Amount = 17;
How can I do this?

This line
List<Person> p = Persons;
does not copy Persons list, it creates an alias for it. Any modification you make to p is actually a modification to Persons list, just by a different alias.
Moreover, Person objects inside the list would need to be copied, too.
Assuming that there is a constructor of Person that takes Person to copy, use this approach:
List<Person> p = Persons.Select(x => new Person(x)).ToList();
If Person does not have a copy constructor, copy it one property at a time:
List<Person> p = Persons.Select(x => new Person {
// I am making up the properties of `Person` to be copied;
// you need to use the actual ones.
Name = x.Name
, LastName = x.LastName
, Address = x.Address
}).ToList();

Related

C# Append One DTO result to another DTO result

I have two DTO classes in a C# MVC web app and I need to return a list generated from one class with a list of data generated by the second within the first list. (Think nested array eg [0[a,b], 1[a,b,c], 2[a,b,c,d], 3,[a], etc...])
This is for an internal application where staff members educate others about their job roles.
The following is what I have so far
//Abstract code representative of end goal
var foo = from x in _fooCtx.Foo
where (x.Condition1 && x.Condition2 && x.Condition3 && x.Condition4 > 0)
select new Models.ResourceDTO()
{
FirstName = x.FirstName,
LastName = x.LastName,
ResourceID = x.ResourceID,
};
//currently loops through each memeber of staff in list above and grabs their wamits history.
foreach (var foobar in foo)
{
int foobarID = foo.ResourceID;
var bar = from u in _barCtx.BarViews
where (u.PupilID == foobarID || u.TeacherID == foobarID)
select new Models.ApplicationDTO()
{
pupilName = u.Pupil,
teacherName = u.Teacher,
appDate = u.appDate.ToString(),
appID = u.ID,
};
var leaderList = bar.ToList();
}
The above works fine in getting a list of active staff members and then looping through any previous sessions they may have done. I need to return the staff list where each member has the list generated by the second DTO select in the foreach loop. So a list of foo data and within this a list of bar data. If this makes sense.
Create a custom DTO
Public class test
{
// add required properties along with list of ApplicationDTO
// so it will be something like this (getter setters)
FirstName
LastName
ResourceID
List<ApplicationDTO>
}
then you can create a list of this test class and use it
P.S : this is just a sample example to help you understand how you can achieve this
You can use Union for appending two dtos with the same type.
This will merge two dtos together:
Dto1.Union(Dto2);

Combining Data from class into a DataTable using Linq

I have a class like so
class Person(){
int Id;
string Name;
string SomePersonalInfo;
}
and a datatable with columns being Id, Name, and SomePersonalInfo. I also have a collection housing data in a List.
List<Person> = new Id = 1, Name = "So So"
List<Person> = new Id = 1, SomePersonalInfo= "Something"
I am trying to use linq so I dont have multiple for each statements to add Name and SomePersonalInfo in the datatable or making unnessary steps like keep getting the datarow. ANy suggestions
If you already have a strongly typed list, why do you want to have a loosely typed DataTable at all? Also, there is no good way to create a DataTable via LINQ-query without having DataRows.
Therefore i would simply use a loop which is readable and efficient:
foreach(Person p in persons)
tblPerson.Rows.Add(p.Id, p.Name, p.SomePersonalInfo);
Update acc. comment:
no I already have the datatable I am just trying to update the
datatable from the collection
Then you have to find the intersection first, use Enumerable.Join:
var inBoth = from p in persons
join row in tblPersons.AsEnumerable()
on p.Id equals row.Field<int>("Id")
select new { Person = p, Row = row };
foreach(var b in inBoth)
{
b.Row.SetField("Name", b.Person.Name);
b.Row.SetField("SomePersonalInfo", b.Person.SomePersonalInfo);
}

Arrays/Array Lists

I am fairly new to C#
I am trying to retrieve some information from an external data source and store it in array, once it is in an array I wish to sort it by time.
I know how to do this for just one column in a row, however the information I require has multiple columns.
For example:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
// Sort my array by Appoint.Start
foreach ( item in myNewArray )
{
//print out Appoint.Subject - Appoint.Start, Appoint.Organiser.Name.ToString() and Appoint.location
}
Many thanks for your help.
EDIT:
I have multiple data sources which pull in this:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
Hence the need to sort the items in a new array, I know this isn't very efficent but there is no way of getting the information I need in any other way.
You can sort a list using the LINQ sorting operators OrderBy and ThenBy, as shown below.
using System.Linq;
and then...
var appointments = new List<Appointment>();
var sortedAppointments = list.OrderBy(l => l.Subject).ThenBy(l => l.Name).ToList();
This will create a new list of appointments, sorted by subject and then by name.
It's unclear what your final aim is but:
Use a generic List instead of an array:
See this SO question for more information as to why using a List is prefered.
List<Appointment> appointments = new List<Appointment>();
foreach (Appointment Appoint in fapts)
{
appointments.Add(Appoint);
}
foreach (var item in appointments)
{
Console.WriteLine(item.Subject);
Console.WriteLine(item.Foo);
// Here you could override ToString() on Appointment to print eveything in one Console.WriteLine
}
If the aim of your code is to order by time, try the following:
var sortedAppointments = fapts.OrderBy(a => a.Start); // assuming Start is a DateTime property of `Appointment`.
Consider a Dictionary Object instead of an array if the data is conceptually one row multiple columns.
foreach(KeyValuePair<string, string> entry in MyDic)
{
// do something with entry.Value or entry.Key
}
You already have a list of objects in fpts, sort that list itself:
fpts.OrderBy(x => x.Subject).ThenBy(x => x.Location).ToList();
LINQ is your friend here.
fapts appears to already be a collection so you could just operate on it.
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start).ToArray()
I've used the ToArray() call to force immediate evaluation and means that myNewArray is already sorted so that if you use it more than once you don't have to re-evaluate the sort.
Alternatively if you are only using this once you can just as easily miss the ToArray() portion out and then execution of the sort will be deferred until you try and enumerate through myNewArray.
This solution puts the source objects into the array, but if you are just wanting to store the specific fields you mention then you will need to use a select. You have two choices for the array item type, you can either use an anonymous class which provides difficulties if you are returning this array from a function or define a class.
For anonymous:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
For named class assuming class is MyClass:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new MyClass {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
You have a wide range of options. The 2 most common are:
1) Create a class, then define an array or list of that class, and populate that
2) Create a structure that matches the data format and create an array or list of that
Of course, you could put the data into an XML format or dataset, but that's probably more work than you need.
public List<foo> appointments = new List<foo>();
public struct foo
{
public string subject ;
public DateTime start ;
public string name ;
public string location ;
}
public void foo1()
{
// parse the file
while (!File.eof())
{
// Read the next line...
var myRecord = new foo() ;
myRecord.subject = data.subject ;
myRecord.start = data.Start ;
myRecord.name = data.Name ;
//...
appointments.Add(myRecord);
}
}
Enjoy
(Since I can't comment and reply to the comment - it wasn't clear if he had a class, etc. or was just showing us what he wanted to do. I assumed it was just for demonstration purposes since there wasn't any info as to how the data was being read. If he could already put it into a class, than the first answer applied anyway. I just tossed the last 2 in there because they were options for getting the data first.)

Best way of building a collection of unique ID's as well as their counts

I've looked into various different ways of array's, arraylist's, dictionaries... but as I'm used to PHP I'm not entirely sure on the best way I could collect the following information.
My program loops through each user, and if their is a location ID, I want to add that to some sort of collection / array. It's expected that different users will have the same location ID.
If the location ID is the same, I need to increase an integer of how many occurrence for that location ID.
Example:
User1 - Location1
User2 - Location3
User3 - Location3
Location1 = 1
Location3 = 2
Also I need to somehow append each user ID to this collection. So Location3 / 2 occurrences / user2/user3
I've been trying to figure out the best way of doing this for about two hours now, and all the different methods of multidimensional arrays, arraylists, dictionaries is all a little confusing as it all seems abstract to my PHP knowledge. I think C# handles arrays in an entirely different way.
Essentially, the collection with unique location ID's / occurrences / and users collection needs to be stored in something that can be passed to somewhere else in my program as an argument.
I've made a PHP script which does exactly what I'm after
foreach($call["data"] as $v)
{
// Foreach USER ($v containing their unique ID and location ID.)
$user_id = $v["id"];
$location_id = $v["location"]["id"];
// This adds the location ID as the key within the array, followed by every user who has it. I don't need a count in this case, as I could just count the number of users.
$collection[$location_id][$user_id] = null;
}
This in return creates this array when printed using print_r
[106078429431815] => Array
(
[620790873] =>
[626276302] =>
[100000152470577] =>
)
(Small part of the output). - Added PHP Example.
Anyone know how I can get C# to collect the same information in the same way my PHP array does?
using System.Linq;
var grouppingByLocation = users.GroupBy(u => u.LocationID);
foreach (var g in grouppingByLocation)
{
Console.WriteLine("Location id: {0}", g.Key);
foreach (var u in g)
{
Console.WriteLine("User id: {0}", u.ID);
}
}
See Enumerable.GroupBy() for more details.
This is an Extension Method over IEnumerable<T> interface implemented by any built-in collection (such as Array T[], List<T>, Dictionary<K,V>, etc.) which accepts a lambda expression pointing to a property of class collection of which you're grouping by.
If you want to build the list looping through initial data, you can create object like this:
var list = new Dictionary<int, Tuple<int, List<int>>();
And fill it in the loop
if(list[locationID]==null) list[locationID] = Tuple.Create(0,new List<int>());
//..
list[locationId].Item1++; // counter
list[locationId].Item2.Add(userId); //list of users
Create an object to hold each item of data.
public Class Model{
public int LocationId {get;set;}
public int Occurences{get;set;}
public IList<User> Users{get;set;}
}
Initialize the container as a list of items.
var container = List<Model>();
Process you list of users.
foreach(var user in userList){
var model = container.SingleOrDefault(x=> x.LocationId == user.LocationId);
if(model != null){
model.Users.Add(user);
} else{
model = new Model{
model.Users = new List<User>.Add(user);
model.LocationId = user.LocationId;
container.Add(model)
}
model.Occruences ++;
}
}
var byLocation = users.Where(u => !string.IsNullOrEmpty(u.Location))
.GroupBy(u => u.Location);
var stats = byLocation.Select(l => string.Format("{0} / {1} occurrences / {2}",
l.Key, l.Count(), string.Join("/", l.Select(u => u.User)));
// And just to print the result
foreach (var location in stats)
Console.WriteLine(location);

What is difference between System.Linq.Enumerable.WhereListIterator & System.Linq.Enumerable.WhereSelectListIterator?

What is difference between System.Linq.Enumerable.WhereListIterator & System.Linq.Enumerable.WhereSelectListIterator?
One difference I hav noticed is Type WhereListIterator reflects changes on collection object but WhereSelectListIterator does not
I will make it more clear for eg.
I hav a scenario where I fetch my Domain Object from Repository
var buckets = testRepository.GetBuckets(testIds);
Then I select certain buckets from the above collection inside a loop
var bucketsForTest = buckets.Where(bucket => bucket.TestID == test.testId);
Then I change a single property of all the Bucket Objects inside the method of LooserTrafficDisributor object.
ITrafficDistributor distributor = new LooserTrafficDisributor(bucketsForTest);
IEnumerable<Bucket> updatedBuckets = distributor.Distribute(test.AutoDecision);
Constructor of LooserTrafficDisributor
public LooserTrafficDisributor(IEnumerable<Bucket> allBuckets)
{
this.allBuckets = allBuckets;
}
The distribute method inside LooserTrafficDistributor looks like this
private IEnumerable<Bucket> DistributeTraffic(bool autoDecision)
{
// allBuckets is class variable in LooserTrafficDistributor object which is set through constructor shown above .
// Omitted other details
allBuckets.Where(bucket=> bucket.IsControl == false).ToList()
.ForEach(bucket => bucket.TrafficPercentage += 10 ));
return allBuckets
}
After this I can see the reflected changes inside the IEnumerable updatedBuckets collection.
But if I do this i.e. instead of fetching Bucket collection from repository do a select & then Update all the Bucket objects in similar manner as follows
var bucketsForTest = testRows.Where(testrow => testrow.url == url.url).Select(currRow => new Bucket
{
TestID = currRow.TestId,
BucketID = currRow.BucketId,
BucketName = currRow.c_bucket_name,
TrafficPercentage = Convert.ToInt32(currRow.i_bucket_percentage),
IsControl = currRow.b_is_control,
IsEnabled = currRow.b_enabled,
UpdatedAdminId = currRow.i_updated_admin_id,
LogAsSection = currRow.i_log_as_section
}) ;
ITrafficDistributor distributor = new LooserTrafficDisributor(bucketsForTest);
IEnumerable<Bucket> updatedBuckets = distributor.Distribute(test.AutoDecision, strategy.GetStatisticallySignificantLoosingBucketIds());
I can't get the changes reflected inside the IEnumerable updatedBuckets collection.
Infact I debugged inside the DistributeTraffic methods even there the changes were not reflected after each loop round.
.Where() makes an IEnumerable of your items containing all elements which fullfil the where criteria. If you run a .Select() on that result set, you will get a IEnumerable of new elements you've created in the select-statement. So changes to the original elements will not reflect on the new elements.
In your example you create for every Bucket in the original list fullfilling your where criteria a new Bucket object, copying the content from the original bucket to the new Bucket.

Categories