I have a two objects, one AnotherList that contains an array of ints and the other MyFolder that contains an array of objects FolderItem that contains an array of objects ItemKeyword that have two strings (in a key-value-pair style).
I want to return a List<FolderItem> from MyFolder that are not referenced in the AnotherList. I can get a list of items that are in the list and have included the Linq for that at the bottom.
I have been fighting with .Contains and .Except extensions all day but keep getting errors. I'm hoping that this is easy for someone.
This is a case where code speaks a thousand words so here it is.
The Linq query at the end returns only one FolderItem at the moment folderItemID=25.
I need it to return all the FolderItems folderItemID=26,27,28 instead.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;
namespace temp
{
class MyFolder
{
public FolderItem[] items { get; set; }
}
class FolderItem
{
public int folderItemID { get; set; }
public ItemKeyword[] keywords { get; set; }
}
class ItemKeyword
{
public string key { get; set; }
public string value { get; set; }
}
class AnotherList
{
public AnotherListItem[] items { get; set; }
}
class AnotherListItem
{
public int dataID { get; set; }
}
public class TestingClass
{
public static void mainApp()
{
AnotherList List1 = new AnotherList()
{
items = new AnotherListItem[]{
new AnotherListItem(){dataID=1},
new AnotherListItem(){dataID=2},
new AnotherListItem(){dataID=3}
}};
MyFolder List2 = new MyFolder()
{
items = new FolderItem[]
{
new FolderItem()
{
folderItemID=25, keywords= new ItemKeyword[]
{
new ItemKeyword(){key="dataID", value="1"},
new ItemKeyword(){key="description", value="some text"},
}
},
new FolderItem()
{
folderItemID=26, keywords= new ItemKeyword[]
{
new ItemKeyword(){key="dataID", value="4"},
new ItemKeyword(){key="description", value="some other text"},
}
},
new FolderItem()
{
folderItemID=27, keywords= new ItemKeyword[]
{
new ItemKeyword(){key="dataID", value="9"},
new ItemKeyword(){key="description", value="even more other text"},
}
},
new FolderItem()
{
folderItemID=28, keywords= new ItemKeyword[]
{
new ItemKeyword(){key="dataID", value="12"},
new ItemKeyword(){key="description", value="3"},
}
}
}};
List<FolderItem> res = (from someItems in List2.items
from itemKeywords in someItems.keywords
join otherItems in List1.items on itemKeywords.value equals otherItems.dataID.ToString()
where itemKeywords.key == "dataID"
select someItems).ToList<FolderItem>();
}
}
}
First grab all of the IDs that we don't want and stick them into a set for fast searching:
var badIDs = new HashSet<int>(List1.items.Select(item => item.dataID));
Then get all of the folders where they are not contained in that set:
var goodFolders = List2.items.Where(folder =>
!badIDs.Contains(folder.folderItemID));
Try using the Any extension method. You can confidently copy and paste the code below since it is meant to work with the code you provided...
List<FolderItem> res = List2.items.Where(x => !List1.items.Any(y => x.keywords.FirstOrDefault(z => z.key == "dataID").value == y.dataID.ToString())).ToList();
var res = List2.items.Where(fi => !List1.items.Any(al =>
al.dataID.ToString() == fi.keywords.Single(k => k.key == "dataID").value));
Assuming a FolderItem only has one ItemKeyword with key="dataID".
Related
I'm trying to figure out how to retrieve objects of a certain type in a List object containing other List objects without having to loop through the entire object tree manually.
I know I can create some kind of loop and check each objects type but I want a more efficient and beautiful way of doing it, like a LINQ statement or some kind of nested lambda expression or whatever other suggestions you might have.
So I have a class object which has a list member, which also has a list member which also has a list member, like so:
TimesheetType
List PeriodType
List Entry
List WorkDay
And I want to retrieve all objects of type WorkDay.
Here's an example code. I created an example object containing 4 WorkDay objects spread out in the tree. How do I retrieve them efficiently?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp
{
public class TimesheetType
{
public List<PeriodType> PeriodTypes { get; set; }
}
public class PeriodType
{
public List<Entry> Entrys { get; set; }
}
public class Entry
{
public List<WorkDay> WorkDays { get; set; }
}
public class WorkDay
{
public DateTime Date { get; set; }
public double Hours { get; set; }
}
class Program
{
static void Main(string[] args)
{
TimesheetType timesheetType = CreateTimesheetTypeExample();
// Get all WorkDay typed objects in the tree, should return a List<WorkDay> with 4 items in it
//var allWorkDays = timesheetType.PeriodTypes.Select(...)
}
private static TimesheetType CreateTimesheetTypeExample()
{
TimesheetType timesheetType = new TimesheetType
{
PeriodTypes = new List<PeriodType>()
{
{
new PeriodType()
},
{
new PeriodType()
},
{
new PeriodType()
},
}
};
timesheetType.PeriodTypes[0].Entrys = new List<Entry>()
{
{
new Entry()
}
};
timesheetType.PeriodTypes[1].Entrys = new List<Entry>()
{
{
new Entry()
}
};
timesheetType.PeriodTypes[2].Entrys = new List<Entry>()
{
{
new Entry()
}
};
timesheetType.PeriodTypes[0].Entrys[0].WorkDays = new List<WorkDay>(1)
{
new WorkDay() { Date = DateTime.Parse("2020-01-01"), Hours = 3 },
new WorkDay() { Date = DateTime.Parse("2020-01-02"), Hours = 6 }
};
timesheetType.PeriodTypes[2].Entrys[0].WorkDays = new List<WorkDay>(1)
{
new WorkDay() { Date = DateTime.Parse("2020-01-03"), Hours = 12 },
new WorkDay() { Date = DateTime.Parse("2020-01-04"), Hours = 24 }
};
return timesheetType;
}
}
}
Assuming PeriodTypes, Entries, WorkDays (so all lists) are never null (but can be empty), couple of SelectMany should do the trick:
var allWorkDays = timesheetType.PeriodTypes
.SelectMany(c => c.Entrys)
.SelectMany(c => c.WorkDays).ToList();
SelectMany flattens nested list, just what you want to do.
In your example though lists can be null, so you need to filter nulls out:
var allWorkDays = timesheetType.PeriodTypes.
Where(c => c.Entrys != null).SelectMany(c => c.Entrys)
.Where(c => c.WorkDays != null).SelectMany(c => c.WorkDays)
.ToList();
Or like this:
var allWorkDays = timesheetType.PeriodTypes
.SelectMany(c => c.Entrys ?? new List<Entry>())
.SelectMany(c => c.WorkDays ?? new List<WorkDay>())
.ToList();
I have a problem in writing my query with C#.
Hee is my data model:
public class SpamEntity:MongoEntity
{
public IList<MessageData> MessageData { get; set; }
}
public class MessageData
{
public IList<string> EmbeddedLinks { get; set; }
}
here I have a list of links like :
var myLinks = {"a.com", "b.com", "c.com"}
I want to filter those documents that their list of EmbededLinks is not empty (count or length is zero) and exactly the same as myLinks.
I am halfway and I do not know what to do in continue.
my filter is something like:
var filter =
Builders<SpamEntity>.Filter.ElemMatch(s => s.MessageData,
s => s.EmbeddedLinks != null && s.EmbededLinks == ???);
I think the below should not be correct.
s.EmbededLinks == myLinks
and also I can not use count:
s.EmbeddedLinks.count => It does not work
Could anyone help me with that?
Not sure about mongodb specific filters..
below is a linq variant (assumption ordering doesnt matter!)
var filtered =
spamEntities.MessageData.Where(
m =>
m.EmbeddedLinks.Any() && //ensure EmbeddedLinks has some values
m.EmbeddedLinks.Count() == myLinks.Count() && //and that count is same as our reference collection
m.EmbeddedLinks.Intersect(myLinks).Count() == myLinks.Count()); //ensure elements match
working code
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace SOProject
{
public class SpamEntity
{
public IList<MessageData> MessageData { get; set; }
}
public class MessageData
{
public IList<string> EmbeddedLinks { get; set; }
}
public class SO
{
[Fact]
public void Q_63081601()
{
var myLinks = new List<string> { "a.com", "b.com", "c.com" };
var size = myLinks.Count();
var data2 = new List<string>(myLinks);
var data3 = new List<string>(myLinks) { "sdsadsad.com" };
var data4 = new List<string> { "c.com", "b.com", "a.com" };
var spamEntities = new SpamEntity()
{
MessageData = new List<MessageData>
{
new MessageData()
{
EmbeddedLinks = new List<string> { "", "aaaa.com", "bbb.com" }
},
new MessageData()
{
EmbeddedLinks = data2
},
new MessageData
{
EmbeddedLinks = Enumerable.Empty<string>().ToList()
},
new MessageData()
{
EmbeddedLinks = data2
},
new MessageData()
{
EmbeddedLinks = data3
},
new MessageData()
{
EmbeddedLinks = data4
}
}
};
var filtered =
spamEntities.MessageData.Where(
m =>
m.EmbeddedLinks.Any() && //ensure EmbeddedLinks has some values
m.EmbeddedLinks.Count() == myLinks.Count() && //and that count is same as our reference collection
m.EmbeddedLinks.Intersect(myLinks).Count() == myLinks.Count()); //ensure elements match
Assert.True(filtered.Any());
Assert.Equal(3, filtered.Count());
}
}
}
Here I have a class List1 and classes List2,ListViewModel for combining two datasets, and I have two different result sets, each list having four values and I need to combine them as a single resultset with 4 rows and need to do the iteration and summation by using the result values in upcoming resultset.
I have tried Both Ways :
Method 1:
var list1 = List1.GetList1();
var list2 = List2.GetList12();
List<ListViewModel> listViewmodelCollection = new List<ListViewModel>();
ListViewModel listViewmodelInstance = new ListViewModel();
foreach (var _list1 in list1)
{
listViewmodelInstance.LocationValues1 = _list1.LocationValues1;
listViewmodelInstance.LocationValues2 = _list1.LocationValues2;
foreach (var _list2 in list2)
{
listViewmodelInstance.LocationValues5 = _list2.LocationValues5;
listViewmodelInstance.LocationValues4 = _list2.LocationValues4;
listViewmodelInstance.RA = _list1.LocationValues1 + _list2.LocationValues4;
listViewmodelCollection.Add(listViewmodelInstance);
}
}
Method2:
List<ListViewModel> listViewmodelCollection = new List<ListViewModel>();
ListViewModel listViewmodelInstance = new ListViewModel();
var x = (from listobj in m.list
from n in m.list2
select new list4
{
LocationValues1 = listobj.LocationValues1,
LocationValues2 = n.LocationValues4,
LocationValues4 = listobj.LocationValues1 + n.LocationValues4
});
-- complete --
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace simple
{
class Program
{
public class List2
{
public string ContinentName { get; set; }
public decimal LocationValues4 { get; set; }
public decimal LocationValues5 { get; set; }
public Nullable<System.DateTimeOffset> CreatedDate { get; set; }
public static List<List2> GetList12()
{
var list2 = new List<List2>
{
new List2{ ContinentName="Asia",LocationValues4=399.23M,LocationValues5=22.90M },
new List2 { ContinentName ="Africa",LocationValues4=199.23M,LocationValues5=22.90M },
new List2 { ContinentName ="Australia",LocationValues4=199.23M,LocationValues5=22.90M },
new List2 { ContinentName ="Pakistan",LocationValues4=199.23M,LocationValues5=22.90M },
};
return list2;
}
}
public class List1
{
public string LocationName { get; set; }
public decimal LocationValues1 { get; set; }
public decimal LocationValues2 { get; set; }
public Nullable<System.DateTimeOffset> CreatedDate { get; set; }
public static List<List1> GetList1()
{
var list1 = new List<List1>
{
new List1 {LocationName="Africa",LocationValues1=199.23M,LocationValues2=22.90M },
new List1 {LocationName="Africa",LocationValues1=299.23M,LocationValues2=24.90M },
new List1 {LocationName="Africa",LocationValues1=399.23M,LocationValues2=25.90M },
new List1 {LocationName="Africa",LocationValues1=499.23M,LocationValues2=26.90M },
};
return list1;
}
}
public class ListViewModel
{
public string LocationName { get; set; }
public decimal LocationValues1 { get; set; }
public decimal LocationValues2 { get; set; }
public Nullable<System.DateTimeOffset> LocationCreatedDate { get; set; }
public decimal RA { get; set; }
public string ContinentName { get; set; }
public decimal LocationValues4 { get; set; }
public decimal LocationValues5 { get; set; }
public Nullable<System.DateTimeOffset> ContinentCreatedDate { get; set; }
}
static void Main(string[] args)
{
var list1 = List1.GetList1();
var list2 = List2.GetList12();
List<ListViewModel> listViewmodelCollection = new List<ListViewModel>();
ListViewModel listViewmodelInstance = new ListViewModel();
foreach (var _list1 in list1)
{
listViewmodelInstance.LocationValues1 = _list1.LocationValues1;
listViewmodelInstance.LocationValues2 = _list1.LocationValues2;
foreach (var _list2 in list2)
{
listViewmodelInstance.LocationValues5 = _list2.LocationValues5;
listViewmodelInstance.LocationValues4 = _list2.LocationValues4;
listViewmodelInstance.RA = _list1.LocationValues1 + _list2.LocationValues4;
listViewmodelCollection.Add(listViewmodelInstance);
}
}
}
Expected Output:
4 Rows
LocationName="Africa",LocationValues1=199.23M,LocationValues2=22.90M,ContinentName="Asia",LocationValues4=399.23M,LocationValues5=22.90M, RA=598.46
LocationName="Africa",LocationValues1=299.23M,LocationValues2=24.90M ,ContinentName ="Africa",LocationValues4=199.23M,LocationValues5=22.90M,RA=465.46
LocationName="Africa",LocationValues1=399.23M,LocationValues2=25.90M ContinentName ="Australia",LocationValues4=199.23M,LocationValues5=22.90M,RA=598.46
LocationName="Africa",LocationValues1=499.23M,LocationValues2=26.90M , ContinentName ="Pakistan",LocationValues4=199.23M,LocationValues5=22.90M.RA=698.46
But current output:
So, this seems messy at best. I'm not sure what your situation is but I would be very nervous about coding to merge 2 different data lists and expecting them to always be equal lengths etc..
I would strongly recommend that you add an interface to both lists so you could at least cast them to a base object and work with them that way instead.
That said I would try to select out the view model attributes via linq from the first set, then iterate through to add the data from the 2nd set and do the computations then.
Example:
var list1 = List1.GetList1();
var list2 = List2.GetList12();
List<ListViewModel> listViewmodelCollection = new List<ListViewModel>();
ListViewModel listViewmodelInstance = new ListViewModel();
listViewmodelCollection.AddRange(list1.Select(l => new ListViewModel()
{
LocationName = l.LocationName,
LocationCreatedDate = l.CreatedDate,
LocationValues1 = l.LocationValues1,
LocationValues2 = l.LocationValues2
}));
for (int i = 0; i < (listViewmodelCollection.Count - 1); i++)
{
var itm2 = list2.ElementAt(i);
if (itm2 != null)
{
listViewmodelCollection[i].ContinentName = itm2.ContinentName;
listViewmodelCollection[i].ContinentCreatedDate = itm2.CreatedDate;
listViewmodelCollection[i].LocationValues4 = itm2.LocationValues4;
listViewmodelCollection[i].LocationValues5 = itm2.LocationValues5;
listViewmodelCollection[i].RA = listViewmodelCollection[i].LocationValues1 + itm2.LocationValues4;
}
}
Given your classes this should get you to the output you wanted, at least for this narrow example.
You can use LINQ to combine the two Lists using the Zip extension method:
var listViewmodelCollection = list1.Zip(list2, (l1, l2) => new ListViewModel {
LocationName = l1.LocationName,
LocationValues1 = l1.LocationValues1,
LocationValues2 = l1.LocationValues2,
ContinentName = l2.ContinentName,
LocationValues4 = l2.LocationValues4,
LocationValues5 = l2.LocationValues5,
RA = l1.LocationValues1+l2.LocationValues4
}).ToList();
I have two classes one of them is Destinations and the other one is DestinationDetails
public class Destinations
{
public Destinations() { }
public string CarrierName { get; set; }
public List<DestinationDetails> Details { get; set; }
}
public class DestinationDetails
{
public DestinationDetails() { }
public string Destination { get; set; }
public string Type { get; set; }
}
I want to get all string "Destination" in the second class from List of objects from the first class
I have List<Destinations> and I don't want to use for loop or foreach statments
var dest = new Destinations();
//Initialize the details
var destNames = dest.Details.Select(d => d.Destination).ToList();
Are you looking for something like this?
var det = new Destinations();
det.Details = new List<DestinationDetails>();
det.Details.Add(new DestinationDetails() { Destination = "CA" });
det.Details.Add(new DestinationDetails() { Destination = "NJ" });
...
...
var details = new DestinationDetails();
details.Destination = string.Join(",",det.Details.Select(x => x.Destination).ToArray() );
Update:-
provided list of Destinations "allDet", you can get the list of strings as below:-
alldet.Where(x => x.Details != null).SelectMany(x => x.Details.Select(y => y.Destination)).ToList() //With out ToList() it will give you IEnumerable<String>
List<Destinations> AirportDestinations ; // this list has Destinations objects which have Details which have Destination
So by using SelectMany
List<string> cities.AddRange(AirportDestinations.Where(x => x.Details != null).SelectMany(d => d.Details.Select(s => s.Destination)));
Now you have all Destination in all objects in the list
I have a problem with Automapper. I set up a test windows form application and below is the code. Also look at the comments after each MessageBox:
public class FirstClass
{
public string FirstProp { get; set; }
public IList<FirstClassChild> Children { get; set; }
}
public class FirstClassChild
{
public string FirstChildProp { get; set; }
}
public class SecondClass
{
public string FirstProp { get; set; }
public string SecondProp { get; set; }
public IList<SecondClassChild> Children { get; set; }
}
public class SecondClassChild
{
public string FirstChildProp { get; set; }
public string SecondChildProp { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
AutoMapper.Mapper.CreateMap<FirstClass, SecondClass>();
AutoMapper.Mapper.CreateMap<FirstClassChild, SecondClassChild>();
var f = new FirstClass { FirstProp = "FirstClass" };
f.Children = new List<FirstClassChild> { new FirstClassChild { FirstChildProp = "FirstClass" } };
var s = new SecondClass { FirstProp = "SecondClass", SecondProp = "SecondClass" };
s.Children = new List<SecondClassChild> { new SecondClassChild { FirstChildProp = "SecondClass", SecondChildProp = "SecondClass" } };
AutoMapper.Mapper.Map(f, s);
var fc = new FirstClassChild { FirstChildProp = "FirstClass" };
var sc = new SecondClassChild { FirstChildProp = "SecondClass", SecondChildProp = "SecondClass" };
AutoMapper.Mapper.Map(fc, sc);
MessageBox.Show(sc.FirstChildProp);//FirstClass as expected
MessageBox.Show(sc.SecondChildProp);//SecondClass as expected
MessageBox.Show(s.FirstProp);//FirstClass as expected
MessageBox.Show(s.SecondProp);//SecondClass as expected
MessageBox.Show(s.Children.First().FirstChildProp);//FirstClass as expected
MessageBox.Show(s.Children.First().SecondChildProp);//Empty not expected!!
}
}
What can I do to avoid this? Is this behavior expected?
Anyway can anyone guide me how make SecondClass childs SecondChildProp to remain "SecondClass" as it is before the mapping occurs.
I asked a similar question here and found another similar one here.
I think #PatrickSteele makes a very good point: how is AutoMapper supposed to map a source list to a dest list of existing objects, when the dest list may not necessarily bear any resemblance to the source list? i.e. "But what if one list has 3 and the other list has 5?"
If you are sure that FirstClass and SecondClass have the same number of Children, and if the FirstClass's Nth Child always corresponds to SecondClass's Nth child, you could try something like this:
Mapper.CreateMap<FirstClass, SecondClass>()
.ForMember(m => m.Children, o => o.Ignore())
.AfterMap((src, dest) =>
{
for (var i = 0; i < dest.Children.Count; i++)
Mapper.Map(src.Children[i], dest.Children[i]);
});
or if FirstChildProp is some kind of unique key:
Mapper.CreateMap<FirstClass, SecondClass>()
.ForMember(m => m.Children, o => o.Ignore())
.AfterMap((src, dest) =>
{
foreach (var dChild in dest.Children)
{
var sChild = src.Children.Single(c => c.FirstChildProp == dChild.FirstChildProp);
Mapper.Map(sChild, dChild);
}
});