How to GROUP BY using Iteration? - c#

I'm trying to GROUP the JSON by BOARD and SUM the likes_count, but not sure how to approach this, since I can only access the Transaction class by looping through Root first?
public class Transaction
{
public string Post { get; set; }
public int board {get; set; }
public int Sent_from { get; set; }
public int likes_count { get; set; }
}
public class Root
{
public int Sent_to { get; set; }
public List<Transaction> Transactions { get; set; }
}
static async System.Threading.Tasks.Task Main(string[] args)
{
var json = "";
using (WebClient wc = new WebClient())
{
json = wc.DownloadString("xxxxxxx");
}
var obj = JsonConvert.DeserializeObject<List<Root>>(json);
foreach (var item in obj)
{
foreach (var child in item.Transactions)
{
// access sent_from, likes_count and post.
}
}
}
I would've normally used:
obj.GroupBy(t => t.post);
Here is a JSON Sample:
Sent_to: X,
total_received_likes: X,
Transactions: [
{
Post: "50776785",
board: "600",
Sent_from: 359716,
likes_count: 4,
},
{
Post: "5085129785",
board: "500",
Sent_from: 359716,
likes_count: 6,
},
{
Post: "506542785",
board: "500",
Sent_from: 359716,
likes_count: 9,
},
The expected output in this case:
15 likes in board 500
4 likes in board 600.

If you want to group the Transactions by board regardless of which Root they belong to and then calculate the sum of likes_count for each group, you may create a list using something like this:
var list = obj.SelectMany(r => r.Transactions)
.GroupBy(t => t.board)
.Select(g => new { Board = g.Key, TotalLikes = g.Sum(t => t.likes_count) })
.ToList();
..which you can then use like this:
foreach (var item in list)
{
Console.WriteLine($"{item.TotalLikes} like(s) in board {item.Board}.");
}
You can also do the same thing for each Root separately if you like:
foreach (var root in obj)
{
var list = root.Transactions
.GroupBy(t => t.board)
.Select(g => new { Board = g.Key, TotalLikes = g.Sum(t => t.likes_count) })
.ToList();
}

Related

Joining two lists of object optimization

I am looking for a way of optimizing my LINQ query.
Classes:
public class OffersObject
{
public List<SingleFlight> Flights { get; set; }
public List<Offer> Offers { get; set; } = new List<Offer>();
}
public class SingleFlight
{
public int Id { get; set; }
public string CarrierCode { get; set; }
public string FlightNumber { get; set; }
}
public class Offer
{
public int ProfileId { get; set; }
public List<ExtraOffer> ExtraOffers { get; set; } = new List<ExtraOffer>();
}
public class ExtraOffer
{
public List<int> Flights { get; set; }
public string Name { get; set; }
}
Sample object:
var sampleObject = new OffersObject
{
Flights = new List<SingleFlight>
{
new SingleFlight
{
Id = 1,
CarrierCode = "KL",
FlightNumber = "1"
},
new SingleFlight
{
Id = 2,
CarrierCode = "KL",
FlightNumber = "2"
}
},
Offers = new List<Offer>
{
new Offer
{
ProfileId = 41,
ExtraOffers = new List<ExtraOffer>
{
new ExtraOffer
{
Flights = new List<int>{1},
Name = "TEST"
},
new ExtraOffer
{
Flights = new List<int>{2},
Name = "TEST"
},
new ExtraOffer
{
Flights = new List<int>{1,2},
Name = "TEST"
}
}
}
}
};
Goal of LINQ query:
List of:
{ int ProfileId, string CommercialName, List<string> fullFlightNumbers }
FullFlightNumber should by created by "Id association" of a flight. It is created like: {CarrierCode} {FlightNumber}
What I have so far (works correctly, but not the fastest way I guess):
var result = sampleObject.Offers
.SelectMany(x => x.ExtraOffers,
(a, b) => {
return new
{
ProfileId = a.ProfileId,
Name = b.Name,
FullFlightNumbers = b.Flights.Select(f => $"{sampleObject.Flights.FirstOrDefault(fl => fl.Id == f).CarrierCode} {sampleObject.Flights.First(fl => fl.Id == f).FlightNumber}").ToList()
};
})
.ToList();
Final note
The part that looks wrong to me is:
.Select(f => $"{sampleObject.Flights.FirstOrDefault(fl => fl.Id == f)?.CarrierCode} {sampleObject.Flights.FirstOrDefault(fl => fl.Id == f)?.FlightNumber}").ToList()
I am basically looking for a way of "joining" those two lists of the OffersObject by Flight's Id.
Any tips appreciated.
If there will only be a few flights defined in sampleObject.Flights, a sequential search using a numeric key is hard to beat.
However, if the number of flights times the number of offers is substantial (1000s or more), I would suggest loading the list of flights into a dictionary with Id as the key for efficient lookup. Something like:
var flightLookup = sampleObject.Flights.ToDictionary(f => f.Id);
And then calculate your FullFlightNumbers as
FullFlightNumbers = b.Flights
.Select(flightId => {
flightLookup.TryGetValue(flightId, out SingleFlight flight);
return $"{flight?.CarrierCode} {flight?.FlightNumber}";
})
.ToList()
TryGetValue above will quietly return a null value for flight if no match is found. If you know that a match will always be present, the lookup cold alternately be coded as:
SingleFlight flight = flightLookup[flightId];
The above also uses a statement lambda. In short, lambda functions can have either expression or statement blocks as bodies. See the C# reference for more information.
I'd suggest replacing the double .FirstOrDefault() approach with .IntersectBy(). It is available in the System.Linq namespace, starting from .NET 6.
.IntersectBy() basically filters sampleObject.Flights by matching the flight ID for each flight in sampleObject with flight IDs in ExtraOffers.Flights.
In the code below, fl => fl.Id is the key selector for sampleObject.Flights (i.e. fl is a SingleFlight).
var result = sampleObject.Offers
.SelectMany(x => x.ExtraOffers,
(a, b) => {
return new
{
ProfileId = a.ProfileId,
Name = b.Name,
FullFlightNumbers = sampleObject.Flights
.IntersectBy(b.Flights, fl => fl.Id)
.Select(fl => fl.FullFlightNumber) // alternative 1
//.Select(fl => $"{fl.CarrierCode} {fl.FlightNumber}") // alternative 2
.ToList()
};
})
.ToList();
In my suggestion I have added the property FullFlightNumber to SingleFlight so that the Linq statement looks slightly cleaner:
public class SingleFlight
{
public int Id { get; set; }
public string CarrierCode { get; set; }
public string FlightNumber { get; set; }
public string FullFlightNumber => $"{CarrierCode} {FlightNumber}";
}
If defining SingleFlight.FullFlightNumber is not possible/desirable for you, the second alternative in the code suggestion can be used instead.
Example fiddle here.

How to put multiple values in Dictionary instances using LINQ?

My Json Response is Following below:
{"d":
{"RowData":
[{"GenreId":11,"GenreName":"Musical","subjecturl":"subjecturl_1","logourl":"logourl_1"},
{"GenreId":12,"GenreName":"kids","subjecturl":"subjecturl_2","logourl":"logourl_2"},
{"GenreId":13,"GenreName":"other","subjecturl":"subjecturl_3","logourl":"logourl_3"},
{"GenreId":14,"GenreName":"Musical","subjecturl":"subjecturl_4","logourl":"logourl_4"},
{"GenreId":15,"GenreName":"Music","subjecturl":"subjecturl_5","logourl":"logourl_5"},
{"GenreId":16,"GenreName":"Faimaly","subjecturl":"subjecturl_6","logourl":"logourl_6"},
{"GenreId":17,"GenreName":"other","subjecturl":"subjecturl_7","logourl":"logourl_7"},
{"GenreId":18,"GenreName":"other","subjecturl":"subjecturl_8","logourl":"logourl_8"},
{"GenreId":19,"GenreName":"kids","subjecturl":"subjecturl_9","logourl":"logourl_9"},
{"GenreId":20,"GenreName":"Musical","subjecturl":"subjecturl_10","logourl":"logourl_10"},
{"GenreId":21,"GenreName":"other","subjecturl":"subjecturl_11","logourl":"logourl_11"}]}}
Using the above Response I tried to make like below Response :
{"rows": [{
"title": "Musical",
"items": [{"hdsubjecturl": "subjecturl_1"},{"hdsubjecturl": "subjecturl_4"},{"hdsubjecturl": "subjecturl_10"}]
},{
"title": "kids",
"items": [{"hdsubjecturl": "subjecturl_2"},{"hdsubjecturl": "subjecturl_9"}]
},{
"title": "Music",
"items": [{"hdsubjecturl": "subjecturl_5"}]
},{
"title": "other",
"items": [{"hdsubjecturl": "subjecturl_3"},{"hdsubjecturl": "subjecturl_7"},{"hdsubjecturl": "subjecturl_8"},{"hdsubjecturl": "subjecturl_11"}]
},{
"title": "Faimaly",
"items": [{"hdsubjecturl": "subjecturl_6"}]
}]
}
My Code is below :
JObject Root = JObject.Parse(jsonData["d"].ToString());
var unique = Root["RowData"].GroupBy(x => x["GenreName"]).Select(x => x.First()).ToList(); // here fetch 5 record
foreach (var un in unique)
{
var GenreName = new
{
title = un["GenreName"],
items = new
{
hdsubjecturl = "logourl"
}
};
var GenreNamereq = JsonConvert.SerializeObject(GenreName, Newtonsoft.Json.Formatting.Indented);
genstr.Append(GenreNamereq, 0, GenreNamereq.Length);
genstr.Append(",");
using (System.IO.StreamWriter file = new System.IO.StreamWriter(subdir + "\\GenreName.json"))
{
string st = genstr.ToString().Substring(0, (genstr.Length - 1));
file.WriteLine("{\n\"rows\": [\n" + st + "\n}"); //seasion number 21 terminate
file.Close();
}
}
Using above code my output is below for First Field :
{"rows":
[{
"title": "Musical",
"items": {
"hdsubjecturl": "logourl"
}
}]
}
Using below code I tried to fetch Multiple values using specific Field :
List<string> CategoryList = new List<string>();
var unique = Root["RowData"].GroupBy(x => x["GenreName"]).Select(x => x.First()).ToList(); // here fetch 8 record
foreach (var cat in unique)
{
CategoryList.Add(cat["GenreName"].ToString());
}
List<List<string>> myList = new List<List<string>>();
for (int i=0;i<CategoryList.Count();i++)
{
var results = from x in Root["RowData"]
where x["GenreName"].Value<string>() == CategoryList[i]
select x;
foreach (var token in results)
{
Console.WriteLine(token["logourl"]);
}
// myList.Add(results);
}
In the First code, I used JObject for fetching a Root node. But, using the above query it takes by default JTocken. So, I used foreach loop here.
I used Dictionary instances for JSON Creation. Using this code I successfully fetched hdsubjecturl in for loop. But, I don't know how to put multiple values in Dictionary instances. Because I get title fields only single times using a unique query and items fields inside a hdsubjetcurl is more than one. Does anyone know how it's possible?
You can group your RowData by GenreName token, then use ToDictionary method to get a result dictionary and map it to desired structure (with title and hdsubjecturl). Finally create a result object using JObject.FromObject
var json = JObject.Parse(jsonString);
var data = json["d"]?["RowData"]
.GroupBy(x => x["GenreName"], x => x["subjecturl"])
.ToDictionary(g => g.Key, g => g.ToList())
.Select(kvp => new { title = kvp.Key, items = kvp.Value.Select(x => new { hdsubjecturl = x }) });
var result = JObject.FromObject(new { rows = data });
Console.WriteLine(result);
It gives you the expected result.
Edit: according to comments, GroupBy and Select expressions should be updated to map more then one property in result title item
var data = json["d"]?["RowData"]
.GroupBy(x => x["GenreName"])
.ToDictionary(g => g.Key, g => g.ToList())
.Select(kvp => new
{
title = kvp.Key,
items = kvp.Value.Select(x => new { hdsubjecturl = x["subjecturl"], url = x["logourl"] })
});
var result = JObject.FromObject(new { rows = data });
Consider trying this code, (using Newtonsoft Json deserializer);
public partial class Root
{
public D D { get; set; }
}
public partial class D
{
public RowDatum[] RowData { get; set; }
}
public partial class RowDatum
{
public long GenreId { get; set; }
public string GenreName { get; set; }
public string Subjecturl { get; set; }
public string Logourl { get; set; }
}
public partial class Response
{
public Row[] Rows { get; set; }
}
public partial class Row
{
public string Title { get; set; }
public Item[] Items { get; set; }
}
public partial class Item
{
public string Hdsubjecturl { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
var json =
#"{""d"":{""RowData"":[{""GenreId"":11,""GenreName"":""Musical"",""subjecturl"":""subjecturl_1"",""logourl"":""logourl_1""},{""GenreId"":12,""GenreName"":""kids"",""subjecturl"":""subjecturl_2"",""logourl"":""logourl_2""},{""GenreId"":13,""GenreName"":""other"",""subjecturl"":""subjecturl_3"",""logourl"":""logourl_3""},{""GenreId"":14,""GenreName"":""Musical"",""subjecturl"":""subjecturl_4"",""logourl"":""logourl_4""},{""GenreId"":15,""GenreName"":""Music"",""subjecturl"":""subjecturl_5"",""logourl"":""logourl_5""},{""GenreId"":16,""GenreName"":""Faimaly"",""subjecturl"":""subjecturl_6"",""logourl"":""logourl_6""},{""GenreId"":17,""GenreName"":""other"",""subjecturl"":""subjecturl_7"",""logourl"":""logourl_7""},{""GenreId"":18,""GenreName"":""other"",""subjecturl"":""subjecturl_8"",""logourl"":""logourl_8""},{""GenreId"":19,""GenreName"":""kids"",""subjecturl"":""subjecturl_9"",""logourl"":""logourl_9""},{""GenreId"":20,""GenreName"":""Musical"",""subjecturl"":""subjecturl_10"",""logourl"":""logourl_10""},{""GenreId"":21,""GenreName"":""other"",""subjecturl"":""subjecturl_11"",""logourl"":""logourl_11""}]}}";
var root = JsonConvert.DeserializeObject<Root>(json);
var rows = root.D.RowData.ToLookup(d => d.GenreName)
.Select(g => new Row()
{
Title = g.Key,
Items = g.ToList().Select(rd => new Item() {Hdsubjecturl = rd.Logourl}).ToArray()
}).ToArray();
var response = new Response()
{
Rows = rows
}; // reponse is the type of Json Response you wanted to achieve
Console.WriteLine();
}
}

Code review of this portion of Linq Filters

I am a newbie of c #, I would like to know if I can remove the for each and do a single operation with Linq. I would like to return an IEnumerable with already filtered. is it possible to do this? Every suggestion is welcome, thank you very much
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Linq
{
class Oggetto
{
public int Id { get; set; }
public string MyProperty { get; set; }
public int Deleted { get; set; }
}
class Program
{
static void Main(string[] args)
{
IEnumerable<Oggetto> lista = new List<Oggetto> {
new Oggetto(){ Id = 1, MyProperty = "Propr1", Deleted = 0 },
new Oggetto(){ Id = 1, MyProperty = "Propr2", Deleted = 1 },
new Oggetto(){ Id = 2, MyProperty = "Prop3", Deleted = 0 },
new Oggetto(){ Id = 3, MyProperty = "Propr4", Deleted = 0 },
new Oggetto(){ Id = 3, MyProperty = "Prop5", Deleted = 1 }
};
foreach (var item in lista.Where(x => x.Deleted == 1).GroupBy(x => x.Id).Select(g => g.First()))
{
item.MyProperty = string.Join(",", lista.Where(t => t.Id == item.Id).Select(x => x.MyProperty).ToArray());
Console.WriteLine(item.Id);
Console.WriteLine(item.MyProperty);
}
Console.ReadLine();
}
}
}
You can use projection for this.
var orderedList = lista.GroupBy(x => x.Id)
.Where(x => x.Any(y => y.Deleted == 1))
.Select(x => new Oggetto
{
Id = x.Key, MyProperty = string.Join(",", x.Select(v => v.MyProperty))
});
foreach (var item in orderedList)
{
Console.WriteLine(item.Id);
Console.WriteLine(item.MyProperty);
}
Anyway, as #Alex said you shoud replace Deleted field type to bool and as said by #Marco Salerno start programming in English you'll not regret.
First of all I would avoid the groupBy statement. This is a lot of unneded overhead. You can use distinct instead. This will give you all the IDs you need to know.
var ids = lista.Where(x => x.Deleted).Select(x => x.Id).Distinct();
You can then select all the elements that you need with:
var items = ids.Select(i => lista.Where(x => x.Id == i));
which results in a List of Lists. For the ease of use I would convert this to a Dictionary<K, V> (int this case it's Dictionary<long, List<string>> as a final step:
var dictionary = items.ToDictionary(l => l.First().Id, l => l.Select(o => o.MyProperty).ToList());
You now got a "nice and filtered" collection you can use any way you like (or just output it)
foreach (var item in dictionary)
{
Console.WriteLine($"Id: {item.Key}");
Console.WriteLine($"Properties: {string.Join(", ", item.Value)}");
}
I also changed your class a little bit to:
class Oggetto
{
public int Id { get; set; }
public string MyProperty { get; set; }
// bool instead of int - Deleted has only 2 states
public bool Deleted { get; set; }
}
First of all STOP programming in Italian, start doing it in English.
Anyway, this should be a better approach:
class Program
{
static void Main(string[] args)
{
List<Item> items = new List<Item> {
new Item{ Id = 1, MyProperty = "Propr1", Deleted = 0 },
new Item{ Id = 1, MyProperty = "Propr2", Deleted = 1 },
new Item{ Id = 2, MyProperty = "Prop3", Deleted = 0 },
new Item{ Id = 3, MyProperty = "Propr4", Deleted = 0 },
new Item{ Id = 3, MyProperty = "Prop5", Deleted = 1}
};
foreach (IGrouping<int,Item> group in items.GroupBy(x => x.Id).ToList())
{
List<Item> groupItems = group.ToList();
Item deletedItem = groupItems.Where(x => x.Deleted == 1).FirstOrDefault();
if(deletedItem != null)
{
deletedItem.MyProperty = string.Join(",", groupItems.Select(x => x.MyProperty).ToArray());
Console.WriteLine(deletedItem.Id);
Console.WriteLine(deletedItem.MyProperty);
}
}
}
}
class Item
{
public int Id { get; set; }
public string MyProperty { get; set; }
public int Deleted { get; set; }
}

How do I use LINQ to group & project objects, based on a member dictionary key?

I have a List<Predictions> that I would like to project onto a List<List<PredictionObj>>. These two classes are defined as follows:
public Predictions
{
public Dictionary<string, double> PredictedMetrics { get; private set; }
public DateTime PredictionTimeStamp { get; set; }
public Predictions()
{
PredictedMetrics = new Dictionary<string, double>();
}
}
public class PredictionObj
{
public string PredictedMetricName { get; set; }
public double PredictedMetricValue { get; set; }
public DateTime PredictionTimeStamp { get; set; }
}
For context, each Predictions object in the List<Predictions> list contains a collection (PredictedMetrics) of predicted values for a set of metrics, which were made at PredictionTimeStamp. I'd like to separate each of those metrics into their own list, such that there will be one list (List<PredictionObj>) for every unique PredictedMetrics key in the list. (PredictedMetricName will map to the dictionary's key, PredictedMetricValue will map to the dictionary's value). I'd like to store all of these lists in one List<List<PredictionObj>> list.
Is there a way to accomplish this using LINQ extension methods?
You can copy/paste the source below into LINQPad as an example. I'm looking for LINQ that will accomplish what GenerateSeperateMetricLists is doing:
void Main()
{
DateTime currTime = DateTime.UtcNow;
List<Predictions> records = new List<Predictions>();
Predictions record1 = new Predictions();
record1.PredictedMetrics.Add("metric1", 2.2d);
record1.PredictedMetrics.Add("metric2", 0.2d);
record1.PredictionTimeStamp = currTime;
records.Add(record1);
Predictions record2 = new Predictions();
record2.PredictedMetrics.Add("metric1", 1.2d);
record2.PredictedMetrics.Add("metric2", 0.1d);
record2.PredictionTimeStamp = currTime.AddMinutes(1);
records.Add(record2);
Predictions record3 = new Predictions();
record3.PredictedMetrics.Add("metric1", 3.2d);
record3.PredictedMetrics.Add("metric2", 0.3d);
record3.PredictionTimeStamp = currTime.AddMinutes(2);
records.Add(record3);
Predictions record4 = new Predictions();
record4.PredictedMetrics.Add("metric1", 4.2d);
record4.PredictedMetrics.Add("metric2", 0.4d);
record4.PredictionTimeStamp = currTime.AddMinutes(3);
records.Add(record4);
//What's the LINQ that could replace this method?
GenerateSeperateMetricLists(records).Dump();
}
private static List<List<PredictionObj>> GenerateSeperateMetricLists(List<Predictions> predictionRecords)
{
var predictionMetricLists = new List<List<PredictionObj>>();
foreach (Predictions forecastRecord in predictionRecords)
{
foreach (KeyValuePair<string, double> prediction in forecastRecord.PredictedMetrics)
{
PredictionObj predictionMetric = new PredictionObj
{
PredictedMetricName = prediction.Key,
PredictedMetricValue = prediction.Value,
PredictionTimeStamp = forecastRecord.PredictionTimeStamp
};
var metricList = predictionMetricLists.Where(x => x.First().PredictedMetricName == prediction.Key);
if (metricList.Count() == 0)
{
predictionMetricLists.Add(new List<PredictionObj> {predictionMetric});
}
else
{
metricList.First().Add(predictionMetric);
}
}
}
return predictionMetricLists;
}
private class Predictions
{
public Dictionary<string, double> PredictedMetrics { get; private set; }
public DateTime PredictionTimeStamp { get; set; }
public Predictions()
{
PredictedMetrics = new Dictionary<string, double>();
}
}
private class PredictionObj
{
public string PredictedMetricName { get; set; }
public double PredictedMetricValue { get; set; }
public DateTime PredictionTimeStamp { get; set; }
}
You should first flatten the data into a list of PredictionObj:
var flatList = records
.SelectMany(r => r.PredictedMetrics.Select(p => new PredictionObj
{
PredictedMetricName = p.Key,
PredictedMetricValue = p.Value,
PredictionTimeStamp = r.PredictionTimeStamp
}));
This produces a flat sequence of PredictionObj objects. Now you can group them by PredictedMetricName:
flatList.GroupBy(x => x.PredictedMetricName).Dump();
The query syntax equivalent, in one statement:
(
from r in records
from p in r.PredictedMetrics
select new PredictionObj
{
PredictedMetricName = p.Key,
PredictedMetricValue = p.Value,
PredictionTimeStamp = r.PredictionTimeStamp
} into flatList
group flatList by flatList.PredictedMetricName into fg
select fg
).Dump();
You just need to take each Prediction and project it into a List<PredictionObj> and then convert those into a List<Prediction>:
var ans = records.SelectMany(p => p.PredictedMetrics.Select(pm => new PredictionObj { PredictedMetricName = pm.Key, PredictedMetricValue = pm.Value, PredictionTimeStamp = p.PredictionTimeStamp }))
.GroupBy(p => p.PredictedMetricName)
.Select(g => g.ToList())
.ToList();
Updated for change in OP.

Automapper overwrites missing source property on list with child objects

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

Categories

Resources