using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConsoleApplication1
{
public class Class1
{
static void Main(string[] args)
{
List<Car> mylist = new List<Car>();
Car car1;
Car car2;
Car car3;
car1 = new Car()
{
make = "Honda",
id = 1
};
car2 = new Car()
{
make = "toyota",
id = 2
};
car3 = new Car()
{
make = "Honda",
id = 3,
color = "red"
};
mylist.Add(car1);
mylist.Add(car2);
**////mylist.Where(p => p.id == 1).SingleOrDefault() = car3;**
}
}
public class Car
{
public int id { get; set; }
public string make { get; set; }
public string color { get; set; }
}
}
How can I update the list by replacing the honda car of Id 1 with honda car with Id 3 in the best way.
Everything leppie said - plus:
int index = mylist.FindIndex(p => p.id == 1);
if(index<0) {
mylist.Add(car3);
} else {
mylist[index] = car3;
}
This just uses the existing FindIndex to locate a car with id 1, then replace or add it. No LINQ; no SQL - just a lambda and List<T>.
If you wanted to do an update to multiple elements...
foreach (var f in mylist.FindAll(x => x.id == 1))
{
f.id = car3.id;
f.color = car3.color;
f.make = car3.make;
}
This is not LINQ2SQL.
Also, LINQ is not used for updating, only to query for objects.
You can use this way :
(from car in mylist
where car.id == 1
select car).Update(
car => car.id = 3);
My reference is this website. Or following is the code for Update method
public static void Update<T>(this IEnumerable<T> source, params Action<T>[] updates)
{
if (source == null)
throw new ArgumentNullException("source");
if (updates == null)
throw new ArgumentNullException("updates");
foreach (T item in source)
{
foreach (Action<T> update in updates)
{
update(item);
}
}
}
As Leppie said, LINQ is for querying rather than updating. However, that can be used to build a new list:
mylist = new List<Car>(from car in mylist select car.id == 1? car3 : car)
That is if you want to use LINQ. It's nice and short code, of course, but a bit less efficient than Marc Gravell's suggestion, as it effectively creates a new list, rather than updating the old one.
//Item class
Class Item
{
public string Name { get; set; }
}
List < Item > myList = new List< Item >()
//Add item to list
Item item = new Item();
item.Name = "Name";
myList.Add(Item);
//Find the item with the name prop
Item item2 = myList.Find(x => x.Name == "Name");
if(item2 != null)
item.Name = "Changed";
Just one question, why do I have to write a Update function for something that seems so basic for a list? There should be standard methods for Lists like Add(), Delete(), Edit(), Insert(), Replace() ....Find()
List<AvailabilityIssue> ai = new List<AvailabilityIssue>();
ai.AddRange(
(from a in db.CrewLicences
where
a.ValidationDate <= ((UniversalTime)todayDate.AddDays(30)).Time &&
a.ValidationDate >= ((UniversalTime)todayDate.AddDays(15)).Time
select new AvailabilityIssue()
{
crewMemberId = a.CrewMemberId,
expirationDays = 30,
Name = a.LicenceType.Name,
expirationDate = new UniversalTime(a.ValidationDate).ToString("yyyy-MM-dd"),
documentType = Controllers.rpmdataController.DocumentType.Licence
}).ToList());
Related
How to combine to generic lists with add range and select criteria? Here is a fictitious example. I can addRange but not with filtering on a criteria like room type. The "Select(mr => mr.Type == RoomType.BedRoom)" does not work. What can I use instead to just append the list where type = BedRoom?
public enum RoomType
{
Bathroom = 1,
BedRoom = 2,
Kitchen = 3,
RecRoom = 4
}
public class RoomsModel
{
public RoomType Type { get; set; }
public int Size { get; set; }
}
public List<RoomsModel> GetRooms(params)
{
var result = new List<RoomsModel>();
result = _service.GetRooms(house1);
var moreRooms _service.GetRooms(house2);
result.AddRange((from mr in moreRooms
select new RoomsModel
{
Type = mr.Type,
Size = mr.Size
}
).Select(mr => mr.Type == RoomType.BedRoom).ToList());
return result;
}
Use Where instead of Select:
result.AddRange(
from mr in moreRooms
where mr.Type == RoomType.BedRoom
select new RoomsModel
{
Type = mr.Type,
Size = mr.Size
});
Where filters items. Select projects item, i.e. transforms each item in the sequence into something else.
You need to use Where instead of Select:
result.AddRange((from mr in moreRooms
select new RoomsModel
{
Type = mr.Type,
Size = mr.Size
}
).Where(mr => mr.Type == RoomType.BedRoom).ToList());
one more way:
result.AddRange(moreRooms.Where(mr => mr.Type == RoomType.BedRoom)
.Select(mr => new RoomsModel {
Type = mr.Type,
Size = mr.Size
}));
I'm created a method that separate the data for an SQLite database into 3 categories:
Modified (variables in list_1 that are not equals to the list_2 ones)
Created (variables in list_1 that are not found in list_2)
Deleted (list_2 variables that are not existing anymore in list_1)
sidenote: list_2 is a backup of list_1 before any modification
The problem with this code is that I can use it only on one class. If I want a second class, then I have to write down the same code again with minor changes. I have now 3 classes, but in the future, I probably want more. It'll be pretty time consuming if I try to write down over and over with every single class, so I posted this question for any suggestion. Also because I didn't find any articles where it uses lambda expressions.
public class Stats
{
public int id { get; set; }
public string name { get; set; }
}
public class FactStats : Stats
{
public string tag { get; set; }
public float balance { get; set; }
public FactStats ShallowCopy()
{
return (FactStats)this.MemberwiseClone();
}
}
List<FactStats> Factions = new List<FactStats>();
List<FactStats> SavedFactions = new List<FactStats>();
void SavetoDatabase()
{
//1. Separate Data
List<FactStats> F_JoinedList = new List<FactStats>();
List<int> F_Modify = new List<int>();
List<int> F_Create = new List<int>();
List<int> F_Delete = new List<int>();
//Modified Objects
F_JoinedList = Factions.Where(n => SavedFactions.Any(o => o.id == n.id)).ToList();
foreach (FactStats f in F_JoinedList)
{
FactStats fs = SavedFactions.Single(x => x.id == f.id);
if (!f.CompareEquals(fs))
F_Modify.Add(f.id);
}
//Created Objects
foreach (FactStats f in Factions)
{
bool vane = Convert.ToBoolean(SavedFactions.Where(f2 => f2.id == f.id).Count());
if (!vane)
F_Create.Add(f.id);
}
//Deleted Objects
foreach (FactStats f in SavedFactions)
{
bool vane = Convert.ToBoolean(Factions.Where(f2 => f2.id == f.id).Count());
if (!vane)
F_Delete.Add(f.id);
}
...
}
I've tried to do it with reflection, not much success. Probably because of my lack of experience.
CompareEquals extensive method (at the Modified Objects) is a third party code that compare two objects of the same class using reflection.
The best way to use one method on different classes is to use Generic method. Since classes are different they should conform to the common interface, for example IUniqueIdentifiable should have "id" property.
You need to create method:
void Save<T>(List<T> saved, List<T> modified) where T: IUniqueIdentifiable
{
List<T> F_JoinedList = new List<T>();
List<int> F_Modify = new List<int>();
List<int> F_Create = new List<int>();
List<int> F_Delete = new List<int>();
//Modified Objects
F_JoinedList = modified.Where(n => saved.Any(o => o.id == n.id)).ToList();
foreach (T f in F_JoinedList)
{
T fs = saved.Single(x => x.id == f.id);
if (!f.CompareEquals(fs))
F_Modify.Add(f.id);
}
//Created Objects
foreach (T f in modified)
{
bool vane = Convert.ToBoolean(saved.Where(f2 => f2.id == f.id).Count());
if (!vane)
F_Create.Add(f.id);
}
//Deleted Objects
foreach (T f in saved)
{
bool vane = Convert.ToBoolean(modified.Where(f2 => f2.id == f.id).Count());
if (!vane)
F_Delete.Add(f.id);
}
...
}
public interface IUniqueIdentifiable
{
id {get;}
}
There are tons of articles how to create Generic method, you can find one sample below
http://www.informit.com/articles/article.aspx?p=605369&seqNum=4
I have IEnumerable collection like following
IEnumerable<Customer> items = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
I want to get value using key Id
I tried like following
public int GetValue(IEnumerable<T> items,string propertyName)
{
for (int i = 0; i < items.Count(); i++)
{
(typeof(T).GetType().GetProperty(propertyName).GetValue(typeof(T), null));
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
}
}
If you want to retrieve a Customer name from a collection by its Id:
public string GetCustomerName(IEnumerable<Customer> customers, int id)
{
return customers.First(c => c.Id == id).Name;
}
Using LINQ you can get all customers names (values) having specific value in this way:
var valuesList = items.Where(x => x.Something == myVar).Select(v => v.Name).ToList();
For single customer name you can do this:
var singleName = items.FirstOrDefault(x => x.Id == 1)?.Name;
Obviously, the Id can be 1, 2 or any other.
Edit:
I recommend you List<Customer> instead of Customer[]
So,
var items = new List<Customer>
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
If I understand you correctly
public static IEnumerable<object> GetValues<T>(IEnumerable<T> items, string propertyName)
{
Type type = typeof(T);
var prop = type.GetProperty(propertyName);
foreach (var item in items)
yield return prop.GetValue(item, null);
}
Just use LINQ to achieve what you want to do. if you want to retrieve a specific value you can use where like this:
public Customer GetCustomerById(IEnumerable<Customer> items,int key)
{
return items.Where(x=>x.id==key)
.Select(x =>x.Name)
.First();
}
this will retrieve the customer who match a specific Id.
Do you want to look things up repeatedly after creating the list? If so, you might want to consider creating a dictionary to do the lookups, like so:
IEnumerable<Customer> items = new Customer[]
{
new Customer {Name = "test1", Id = 999},
new Customer {Name = "test2", Id = 989}
};
var lookup = items.ToDictionary(itemKeySelector => itemKeySelector.Id);
var result = lookup[989];
Console.WriteLine(result.Name); // Prints "test2".
I'm assuming that you don't create the collection in the first place - if you had control over creating the original collection you could use a dictionary in the first place.
private TextBox [] Collectionstextboxonpanel(Panel panel)
{
var textBoxspanel1 = panel.Controls.OfType<TextBox>(); // select controls on panle1 by type
IEnumerable<TextBox> textBoxes = textBoxspanel1; // create collection if need
TextBox[] textBoxes1 = textBoxes.ToArray(); // Array collection
return textBoxes1; // get back TextBox Collection
}
I have a custom list, shown below.
class ActionToDo
{
public string Name {get;set;}
public DateTime dtDate {get;set;}
public string EventCode {get;set;}
public string EventDescription {get;set;}
}
What I would like to do is find items which have the same date & same event description and where the EventCode = "AQ". I am guessing LINQ is the best way to achieve this? I do not know how to do this though using LINQ.
In my head using Sql I think it would roughly be something like below.
SELECT * FROM SomeTable
WHERE [EventDescription] = 'AQ'
GROUP BY [dtDate], [EventDescription]
.Where(x=> x.EventDescription == "AQ")
.GroupBy(x => new { x.dtDate.Date, x.EventDescription})
So let's assume you have a
List<ActionToDo> actionToDo = new List<ActionToDo>();
actionToDo.Where(i => i.EventDescription == "AQ" && i.EventCode="AQ")
.GroupBy(i => new { i.dtDate.Date, i.EventDescription });
Here is a LINQ query expressed using query syntax that meets your requirements. In addition, it also sorts the grouping properties dtDate and EventDescription and then the members of each group by Name:
var actions = new List<ActionToDo>();
// populate 'actions'
var results =
from a in actions
where a.EventCode == "AQ"
orderby a.dtDate, a.EventDescription, a.Name
group a by new { a.dtDate, a.EventDescription };
To demonstrate this query, I created a program with some sample ActionToDo data in random order. Please see below for the formatted output from the program followed by the program itself.
Demonstration Program Output
[2014-02-12] [Desc.AQ.12]
AQ.12a
AQ.12b
[2014-02-13] [Desc.AQ.13]
AQ.13a
AQ.13b
AQ.13c
[2014-02-14] [Desc.AQ.14]
AQ.14a
AQ.14b
Demonstration Program
using System;
using System.Collections.Generic;
using System.Linq;
class GroupByDemo
{
static public void Main(string[] args)
{
var actions = new List<ActionToDo>()
{
new ActionToDo("AQ.14b", "2014-02-14", "AQ", "Desc.AQ.14"),
new ActionToDo("AQ.12a", "2014-02-12", "AQ", "Desc.AQ.12"),
new ActionToDo("AQ.13b", "2014-02-13", "AQ", "Desc.AQ.13"),
new ActionToDo("XX.01", "2014-02-01", "XX", "Desc.XX.01"),
new ActionToDo("AQ.14a", "2014-02-14", "AQ", "Desc.AQ.14"),
new ActionToDo("AQ.12b", "2014-02-12", "AQ", "Desc.AQ.12"),
new ActionToDo("AQ.13a", "2014-02-13", "AQ", "Desc.AQ.13"),
new ActionToDo("XX.02", "2014-02-02", "XX", "Desc.XX.02"),
new ActionToDo("AQ.13c", "2014-02-13", "AQ", "Desc.AQ.13"),
new ActionToDo("XX.03", "2014-02-03", "XX", "Desc.XX.03")
};
var results =
from a in actions
where a.EventCode == "AQ"
orderby a.dtDate, a.EventDescription, a.Name
group a by new { a.dtDate, a.EventDescription };
foreach (var group in results)
{
Console.WriteLine("[{0}] [{1}]",
group.Key.dtDate.ToString("yyyy-MM-dd"),
group.Key.EventDescription);
foreach (var action in group)
{
Console.WriteLine(" {0}", action.Name);
}
}
}
}
class ActionToDo
{
public string Name {get;set;}
public DateTime dtDate {get;set;}
public string EventCode {get;set;}
public string EventDescription {get;set;}
public ActionToDo(
string name,
string dtDateString,
string eventCode,
string eventDescription)
{
this.Name = name;
this.dtDate = DateTime.Parse(dtDateString);
this.EventCode = eventCode;
this.EventDescription = eventDescription;
}
}
I have a function that reads a file in chunks.
public static DataObject ReadNextFile(){ ...}
And dataobject looks like this:
public DataObject
{
public string Category { get; set; }
// And other members ...
}
What I want to do is the following basically
List<DataObject> dataObjects = new List<DataObject>();
while(ReadNextFile().Category == "category")
{
dataObjects.Add(^^^^^ the thingy in the while);
}
I know it's probably not how it's done, because how do I access the object I've just read.
I think what you're looking for is:
List<DataObject> dataObjects = new List<DataObject>();
DataObject nextObject;
while((nextObject = ReadNextFile()).Category == "category")
{
dataObjects.Add(nextObject);
}
But I wouldn't do that. I'd write:
List<DataObject> dataObject = source.ReadItems()
.TakeWhile(x => x.Category == "Category")
.ToList();
where ReadItems() was a method returning an IEnumerable<DataObject>, reading and yielding one item at a time. You may well want to implement it with an iterator block (yield return etc).
This is assuming you really want to stop reading as soon as you find the first object which has a different category. If you actually want to include all the matching DataObjects,
change TakeWhile to Where in the above LINQ query.
(EDIT: Saeed has since deleted his objections to the answer, but I guess I might as well leave the example up...)
EDIT: Proof that this will work, as Saeed doesn't seem to believe me:
using System;
using System.Collections.Generic;
public class DataObject
{
public string Category { get; set; }
public int Id { get; set; }
}
class Test
{
static int count = 0;
static DataObject ReadNextFile()
{
count++;
return new DataObject
{
Category = count <= 5 ? "yes" : "no",
Id = count
};
}
static void Main()
{
List<DataObject> dataObjects = new List<DataObject>();
DataObject nextObject;
while((nextObject = ReadNextFile()).Category == "yes")
{
dataObjects.Add(nextObject);
}
foreach (DataObject x in dataObjects)
{
Console.WriteLine("{0}: {1}", x.Id, x.Category);
}
}
}
Output:
1: yes
2: yes
3: yes
4: yes
5: yes
In other words, the list has retained references to the 5 distinct objects which have been returned from ReadNextFile.
This is subjective, but I hate this pattern (and I fully recognize that I am in the very small minority here). Here is how I do it when I need something like this.
var dataObjects = new List<DataObject>();
while(true) {
DataObject obj = ReadNextFile();
if(obj.Category != "category") {
break;
}
dataObjects.Add(obj);
}
But these days, it is better to say
List<DataObject> dataObjects = GetItemsFromFile(path)
.TakeWhile(x => x.Category == "category")
.ToList();
Here, of course, GetItemsFromFile reads the items from the file pointed to by path and returns an IEnumerable<DataObject>.
List<DataObject> dataObjects = new List<DataObject>();
string category = "";
while((category=ReadNextFile().Category) == "category")
{
dataObjects.Add(new DataObject{Category = category});
}
And if you have more complicated object you can do this (like jon):
List<DataObject> dataObjects = new List<DataObject>();
var category = new DataObject();
while((category=ReadNextFile()).Category == "category")
{
dataObjects.Add(category);
}
You should look into implementing IEnumerator on the class container the call to ReadNextFile(). Then you would always have reference to the current object with IEnumerator.Current, and MoveNext() will return the bool you are looking for to check for advancement. Something like this:
public class ObjectReader : IEnumerator<DataObject>
{
public bool MoveNext()
{
// try to read next file, return false if you can't
// if you can, set the Current to the returned DataObject
}
public DataObject Current
{
get;
private set;
}
}