Merge Two List and Sort [duplicate] - c#

This question already has answers here:
Joining two lists together
(15 answers)
Closed 2 years ago.
class A
{
int id,
DateTime joinDate,
string name
}
class B
{
int uniqueId,
DateTime joinDate,
}
I have two lists with the above objects
List<A> listofA
List<B> listofB
I want to
merge these two lists into a new list and then sort the new list by DateTime
which is common to both lists.
I have followed the this link to understand the merge logic.
Kindly do provide a feasible solution to understand such sorting logic
So far the result
List<object> mergedList = (from x in A select (Object)x).ToList();
mergedList.AddRange((from x in B select (Object)x).ToList());
mergedList.Orderby()??? based on joinDate

Something like this could work:
var m = A.Select(a => new { a.JoinDate, O = (object)a })
.Concat(B.Select(b => new { b.JoinDate, O = (object)b }))
.OrderBy(c => c.JoinDate);
But note that for each item in the list you're then going to have to ask if O is a ClassA or a ClassB
for(var x in m){
if(x.O is ClassA)
...
}
I do think the suggestions in the comments that these classes should descend from a common ancestor or implement a common interface would make them easier to work with..

As noted you can't merge two disparate class types without resorting to using the object class, which I'd recommend against as it is a potential source of introducing bugs.
Better to give each class a base interface and work with a list based on that.
You then need to compare your property type in order to determine how best to sort it.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<ClassA> listA = new List<ClassA>()
{
new ClassA(){Id=1, JoinDate = new DateTime(2013, 6, 1, 12, 32, 30)},
new ClassA(){Id=2, JoinDate = new DateTime(2021, 7, 2, 12, 32, 30)},
};
List<ClassB> listB = new List<ClassB>()
{
new ClassB(){UniqueId=23, JoinDate = new DateTime(2021, 2, 6, 8, 32, 30)},
new ClassB(){UniqueId=47, JoinDate = new DateTime(2017, 2, 6, 8, 32, 30)},
};
List<IJoinDate> listJoinDate = new List<IJoinDate>();
foreach(var item in listA)
{
listJoinDate.Add(item);
}
foreach(var item in listB)
{
listJoinDate.Add(item);
}
listJoinDate.Sort((x,y)=>
{
if (x.JoinDate == null && y.JoinDate == null) return 0;
else if (x.JoinDate == null) return -1;
else if (y.JoinDate == null) return 1;
else return x.JoinDate.CompareTo(y.JoinDate);
});
Console.WriteLine("Total records = %d", listJoinDate.Count);
foreach(var item in listJoinDate)
{
Console.WriteLine(item.JoinDate);
}
Console.WriteLine("Linq OrderByDescending");
listJoinDate.AsQueryable().OrderByDescending(x=>x.JoinDate);
foreach(var item in listJoinDate)
{
Console.WriteLine(item.JoinDate);
}
Console.WriteLine("Linq OrderBy");
listJoinDate.AsQueryable().OrderBy(x=>x.JoinDate);
foreach(var item in listJoinDate)
{
Console.WriteLine(item.JoinDate);
}
}
}
public interface IJoinDate
{
DateTime JoinDate{get;set;}
}
public class ClassA :IJoinDate
{
public int Id{get;set;}
public DateTime JoinDate{get;set;}
}
public class ClassB :IJoinDate
{
public int UniqueId{get;set;}
public DateTime JoinDate{get;set;}
public string Name{get;set;}
}

Populate your lists, listofA and listofB and use following queries.
Order by Ascending
var mergedList = (from elementA in listofA select new { ID = elementA.id, JoinDate = elementA.joinDate, Name = elementA.name })
.Concat(from elementB in listofB select new { ID = elementB.uniqueId, JoinDate = elementB.joinDate, Name = string.Empty })
.OrderBy(x => x.JoinDate);
Order by Descending
var mergedList = (from elementA in listofA select new { ID = elementA.id, JoinDate = elementA.joinDate, Name = elementA.name })
.Concat(from elementB in listofB select new { ID = elementB.uniqueId, JoinDate = elementB.joinDate, Name = string.Empty })
.OrderByDescending(x => x.JoinDate);

Related

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 to combine to generic lists with add range and select criteria?

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
}));

Understanding LINQ subquery

I used a normal query inside a foreach to reach my goal, but I think there is a better way.
int max = 0;
foreach(Area area in myZoo.AreaList)
{
max = (from a in area.AnimalList
select a.ID).Max();
}
return max;
How can I use a pure LINQ to get the max of all sublists? (Animal is a List of the class Animal inside the AreaList and contains an Int32 called ID) Erm, and Lambda is welcome, so don't hastitate to answer only because you only know the lambda answer ;)
public class Zoo
{
public List<Area> AreaList {get; set;}
}
public class Area
{
public List<Animal> AnimalList {get; set;}
}
public class Animal
{
public List<int> Id {get; set;}
}
Only the short form so no one gets confused ;)
Although SelectMany works, I feel query is more readable in such case.
var max = (from area in myZoo.AreaList
from animal in area.AnimalList
from id in animal.Id
select id).Max();
You are looking for a nested SelectMany.
SelectMany will return a single IEnumerable<T> from the many "inner" Ienumerable<T> - so Zoo.SelectMany(a => a.AreaList) will return a single IEnumerable<Area> containing all the IEnumerable<Area> from the Area property - then you do it one more time for the List<Animal> in the Area class:
Sample data:
var zoo = new Zoo() {
AreaList = new List<Area>()
{
new Area()
{
AnimalList = new List<Animal>()
{
new Animal() {Id = new List<int>() {1, 2, 3}},
new Animal() {Id = new List<int>() {4, 5, 6}}
}
},
new Area()
{
AnimalList = new List<Animal>()
{
new Animal() {Id = new List<int>() {7, 8, 9}},
new Animal() {Id = new List<int>() {10, 11}}
}
},
}
};
Linq query:
var max = zoo.AreaList.SelectMany(a => a.AnimalList).SelectMany(a => a.Id).Max();
Console.WriteLine(max);
Result: 11
In query syntax you can do SelectMany by chaining from clauses, as shown in gxp's answer. (Personally, I prefer the method chaining syntax, so it took me some time to figure that one out...)
var max = zoo.AreaList.Max(arl => arl.AnimalList.Max(anl => anl.Id)).Max();
The maximum value of all maximum values of all AnimalLists, and the Max of them.
That's effectievly the same as ZoharPeled's SelectMany, except he flattens the list and takes the max of all items where I take the max of each list again and again.

Is it possible to update list of objects except the MIN value in this list in one LINQ statement?

I think my question is from functional programming.
By the way I wrote some code for updating list of my objects:
var minDate = userRecords.Min(x => x.FromDate);
return userRecords.Where(x => x.FromDate != minDate).Select(x =>
{
x.IsInitialRecord = false;
return x;
});
I want to rewrite it without using local variable.
Thanks in advance!
I agree with Ivan's comment that mutating state in that context is prone to end up in code smell.
But if one would insist to go that road, I'd at least try to encapsulate the state mutation, as in e.g.,
(contrived)
public class UserRecord
{
public UserRecord(DateTime fromDate)
{
FromDate = fromDate;
AfterUpdate(true);
}
public UserRecord AfterUpdate(bool initialRecord)
{
IsInitialRecord = initialRecord;
return this;
}
public DateTime FromDate { get; private set; }
public bool IsInitialRecord { get; private set; }
}
public static void Main(string[] args)
{
var userRecords =
new[]
{
new UserRecord(new DateTime(1900, 1, 1)),
new UserRecord(new DateTime(1801, 1, 1)),
new UserRecord(new DateTime(1913, 1, 1)),
new UserRecord(new DateTime(1850, 1, 1))
};
var updatedRecords =
(
from minDate in new[] { userRecords.Min(r => r.FromDate) }
from record in userRecords
where record.FromDate > minDate
select record.AfterUpdate(false)
);
foreach (var record in updatedRecords)
{
Console.WriteLine("{0} ({1})", record.FromDate, record.IsInitialRecord);
}
// Etc...
}
'HTH,

List Manipulation in C# using Linq

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());

Categories

Resources