I have two lists which i want to get the different items from them
SearchElement[] criteria = new SearchElement[] {
new SearchElement
{
Comparison = "=",
FieldName = "CableProperty.ProjectId",
FieldValue = int.Parse(comboBoxSource.SelectedValue.ToString()),
LogicalOperator = "" }
};
sourceCables = client.GetCables(criteria, null, "Cores,CableProperty,CableProperty.CableApplication").ToList();
criteria = new SearchElement[] {
new SearchElement
{
Comparison = "=",
FieldName = "CableProperty.ProjectId",
FieldValue = int.Parse(comboBoxDestination.SelectedValue.ToString()),
LogicalOperator = "" }
};
destinationCables = client.GetCables(criteria, null, "Cores,CableProperty,CableProperty.CableApplication").ToList();
diffCables = sourceCables.Except(destinationCables, new CableComparer())
.ToList();
Now I have the different items in diffcable. Sometimes i want to set
diffCable.CableProperty.CableApplication = null;
but when i do that, all the navigation Porperty(CableApplication) in sourcelist is also set to null.
this is the code
if (destinationCableApplications.Contains(diffCable.CableProperty.CableApplication, new CableApplicationComparer()))
{
criteria = new SearchElement[] { new SearchElement { Comparison = "=", FieldName = "ProjectId", FieldValue = int.Parse(comboBoxDestination.SelectedValue.ToString()), LogicalOperator = "" }};
cableApplication = client.GetCableApplications(criteria, null, "").SingleOrDefault();
diffCable.CableProperty.CableApplication = null;
}
excatly in after this line
diffCable.CableProperty.CableApplication = null;
all the
sourcecables[0].CableProperty.CableApplication
sourcecables[1].CableProperty.CableApplication
.....
sourcecables[100].CableProperty.CableApplication
are set to null
what should i do to not lose the navigation property in sourcelist when i set null to navigation property in diffcable ?
easiest way is using MemoryStream..
Here is a sample,
[Serializable]
public class temp
{
public int a;
}
class Program
{
public static T DeepClone<T>(T a)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, a);
stream.Position = 0;
return (T)formatter.Deserialize(stream);
}
}
static void Main(string[] args)
{
List<temp> list1 = new List<temp>();
list1.Add(new temp { a = 1 });
list1.Add(new temp { a = 2 });
list1.Add(new temp { a = 3 });
List<temp> list2 = DeepClone<List<temp>>(list1);
list1[1].a = 4;
Console.WriteLine(list2[1].a);
Console.ReadKey();
}
}
Note: class must be Serializable.
This will work for all value and reference types.
You are doing a copy by reference without realising it. Look into Cloning your objects or creating new lists.
List<int> newCopyList = new List<int>(originalList);
Related
I am web-scraping some data and trying to write the scraped data to a json file using C# newtonsoft.Json
I get stuck when writing a foreach in my .ToDictionary function as well as not being able to ++ an index into my .ToDictionary function.
My class:
public class JsonParametersData
{
public bool required { get; set; }
public bool list { get; set; }
public List<string> options { get; set; }
}
My arrays
var jsonData = new List<Dictionary<string, Dictionary<string, JsonParametersData>>>();
var moduleParameters = new List<string>();
var parameterOptionsArray = new List<List<string>>();
var parameterOptions = new List<string>();
var requiredArray = new List<bool>();
var listArray = new List<bool>();
string moduleName = item.Attributes["href"].Value.Replace("_module.html", "");
The code which is commented shows what I am trying to do.
int index = 0;
jsonData.Add(new Dictionary<string, Dictionary<string, JsonParametersData>>()
{
{
moduleName,
moduleParameters
.ToDictionary(n => n,
n => new JsonParametersData
{
required = requiredArray[index],
list = listArray[index],
options = new List<string>() { "option1", "option2" },
/*
foreach (var parameteroption in parameterOptionsArray[index])
{
options.Add(parameteroption);
}
index++;
*/
})
}
});
string json = JsonConvert.SerializeObject(jsonData.ToArray());
//write string to file
System.IO.File.WriteAllText(#"path", json);
Your parameterOptionsArray is not an Array, but a List of lists.
The thing is that parameterOptionsArray[index] is a List, not a string. So you should use AddRange() instead of Add().
parameterOptionsArray.Foreach(parameteroption => options.AddRange(parameteroption));
As I´ve written in the comments you can make only assignments in an object-initializer. Thus the following is allowed:
var a = new { MyMember = anInstance }
whilst this is not:
var a = new { MyMember = anInstance, anInstance.DoSomething() };
That´s one of those cases where you should not use Linq at all, as it leads to more confusion than it helps. Instead use a good old-styled loop:
int index = 0;
var innerDict = new Dictionary<string, JsonParametersData>();
foreach(var name in moduleParameters)
{
innerDict[name] = new JsonParametersData
{
required = requiredArray[index],
list = listArray[index],
options = new List<string>() { "option1", "option2" },
}
innerDict[name].Options.AddRange(parameterOptionsArray[index]);
index++;
}
var dict = new Dictionary<string, Dictionary<string, JsonParametersData>>();
dict[moduleName] = innerDict;
jsonData.Add(dict);
string json = JsonConvert.SerializeObject(jsonData.ToArray());
You appear to have a jagged array in parameterOptionsArray. You can make use of SelectMany here. Perhaps following sample can help:
string[][] parameterOptionsArray = new string[2][];
parameterOptionsArray[0] = new string[2];
parameterOptionsArray[0][0] = "1";
parameterOptionsArray[0][1] = "2";
parameterOptionsArray[1] = new string[2];
parameterOptionsArray[1][0] = "3";
parameterOptionsArray[1][1] = "4";
var testing = new {options = parameterOptionsArray.SelectMany(x => x).ToList()};
testing.options.ForEach(x => Console.WriteLine(x));
I'm having trouble with Nested aggregations. Here is how I define my aggregations and the json equivalent I get:
var aggregations = new AggregationDictionary();
var nestedAgg = new AggregationContainer
{
Nested = new NestedAggregation("some_name1")
{
Path = "users",
Aggregations = new TermsAggregation("some_name2")
{
Field = "users.name.keyword",
Size = 100,
Order = new List<TermsOrder> { new TermsOrder() { Key = "_term", Order = SortOrder.Descending } }
}
};
aggregations[aggKey] = nestedAgg;
searchRequest.Aggregations = aggregations;
The above is translated to the following json. it only shows the path and no aggregations definition:
{
"aggs": {
"some_name1": {
"nested": {
"path": "users"
}
}
}
Using AggregationDictionary and AggregationContainer directly is a little awkward; You can do so like this
var aggregations = new AggregationDictionary();
var aggKey = "some_name1";
AggregationContainer nestedAgg = new NestedAggregation("some_name1")
{
Path = "users",
Aggregations = new TermsAggregation("some_name2")
{
Field = "users.name.keyword",
Size = 100,
Order = new List<TermsOrder>
{
new TermsOrder() { Key = "_term", Order = Nest.SortOrder.Descending }
}
}
};
aggregations[aggKey] = nestedAgg;
var searchRequest = new SearchRequest<Test>();
searchRequest.Aggregations = aggregations;
client.Search<Test>(searchRequest);
Here we use the implicit conversion from AggregationBase (the base class that aggregations derive from) to AggregationContainer to assign a NestedAggregation to a variable of type AggregationContainer.
This implicit conversion also sets the Aggregations and Meta properties on AggregationContainer, which is why your original attempt doesn't quote work correctly.
Using EPPlus I want to load data horizontally.
var randomData = new[] { "Foo", "Bar", "Baz" }.ToList();
ws.Cells["B4"].LoadFromCollection(randomData);
Default behaviour is vertically, this code will result in:
This is what I need:
Downside of using EPPlus, their documentation is sketchy.
What if you did something like this:
var randomData = new[] { "Foo", "Bar", "Baz" }.ToList();
//ws.Cells["B4"].LoadFromCollection(randomData);
ws.Cells["B4"].LoadFromArrays(new List<string[]>(new[] { randomData.ToArray() }));
Which gives me this in the output:
Bear in mind that if you are concerned about performance, say with very large collections, you are better off writing your own code anyway as the LoadFrom* methods do add overhead to account for multiple scenarios.
If I am condemned to looping myself, I can write the code:
public byte[] TestExcellGeneration_HorizontalLoadFromCollection()
{
byte[] result = null;
using (ExcelPackage pck = new ExcelPackage())
{
var foo = pck.Workbook.Worksheets.Add("Foo");
var randomData = new[] { "Foo", "Bar", "Baz" }.ToList();
//foo.Cells["B4"].LoadFromCollection(randomData);
int startColumn = 2; // "B";
int startRow = 4;
for(int i = 0; i < randomData.Count; i++)
{
foo.Cells[startRow, startColumn + i].Value = randomData[i];
}
result = pck.GetAsByteArray();
}
return result;
}
And when you call this from a TestMethod:
[TestMethod]
public void TestExcellGeneration_HorizontalLoadFromCollection()
{
var excelFileBytes = (new MyExcelGenerator()).TestExcellGeneration_HorizontalLoadFromCollection();
OpenExcelFromTempFile(excelFileBytes);
}
private void OpenExcelFromTempFile(byte[] data)
{
string tempPath = System.IO.Path.GetTempFileName();
System.IO.File.WriteAllBytes(tempPath, data);
Application excelApplication = new Application();
_Workbook excelWorkbook;
excelWorkbook = excelApplication.Workbooks.Open(tempPath);
excelApplication.Visible = true;
}
It results in:
Here is an extension method:
public static void LoadFromCollectionHorizontally<T>(this ExcelWorksheet excelWorksheet, IEnumerable<T> objects, string cellAddress = "A1")
{
List<object[]> valuesHorizontally = new List<object[]>();
if (typeof(T).IsClass)
{
var properties = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => !Attribute.IsDefined(p, typeof(EpplusIgnore)));
foreach (var prop in properties)
{
var values = new List<object>();
foreach (T item in objects)
{
values.Add(prop.GetValue(item));
}
valuesHorizontally.Add(values.ToArray());
}
}
else
{
valuesHorizontally.Add(objects.Cast<ValueType>().ToArray());
}
var startingCellRange = excelWorksheet.Cells[cellAddress];
var filledUpCellRange = startingCellRange.LoadFromArrays(valuesHorizontally);
...
}
I have some object models like this :
var x= new XModel()
{
end_date = "2017-12-15",
page = 1,
start_date = "2014-12-01",
group_by = new List<string> { "numbers" },
filter = new Filter() { numbers= new List<int> {1620} ,names= null, deleted= null, added = null }
};
or this one :
var y= new YModel
{
Title = "test title",
GenderType = Gender.Men,
Oss = "ALL",
Devices = new List<string> { "11", "12" },
Categories = new List<string> { "11", "12" },
}
i want to add these models to the list, the problem is , i tried to wrote a generic method to add all object models like above to the list.
My current method is :
internal static List<UrlParameter> GetParams<TModel>(this TModel entity)
{
var parameters = new List<UrlParameter>();
foreach (var propertyInfo in entity.GetType().GetProperties())
{
var propVal = propertyInfo.GetValue(entity, null);
if (propVal == null)
{
parameters.Add(new UrlParameter(propertyInfo.Name, ""));
continue;
}
if (propertyInfo.PropertyType.IsGenericType)
{
if (propVal.GetType().IsPrimitiveType())
{
parameters.Add(new UrlParameter(propertyInfo.Name, propVal));
}
else
{
var arr = propVal as IEnumerable;
if (arr.HasArrayContainPrimitiveType())
parameters.Add(new UrlParameter(propertyInfo.Name, $"[{ToJsonArray(arr)}]"));
else
parameters.AddRange(from object obj in arr
select GetParams(obj)
into subparams
select new UrlParameter(propertyInfo.Name, subparams));
}
}
else
{
if (propVal.GetType().IsPrimitiveType())
parameters.Add(new UrlParameter(propertyInfo.Name, propVal));
else
{
var subparams = GetParams(propVal);
parameters.Add(new UrlParameter(propertyInfo.Name, subparams));
}
}
}
return parameters;
}
it works fine for most of my models, but the x where contains filter makes me a problem, the numbers value saved like this filter=numbers%3d%255b1620%255d%2c%2c%2c%2c%2c%2c%2c , and the rest of the fields disappeare.
i want to add numbers, names, deleted and added as key, value parameter nested in filter, can you please help me to fixed this?
I solved my problem by using MultipartFormDataContent class.
it converts all the nested model as they are.
I'm searching a sorted dictionary with a key of type datetime and values as list of objects. What I need to find is the latest value(based on a property on the object) for each object in the dictionary. My object has 3 properties : a name, a value and a date when it was created. My dictionary is sorted by latest date in descending order.
I have got this working somehow, but I'm sure there is a shortcut for this using LINQ. Please note that I'm using .NET 3.5. Could you please help? Please dont get put off by the huge amount code below as I have added it for clarity and i'm only looking for a linq query to query inside a list of list objects.
Code below:
public void Should_link_recent_data_together()
{
var data = TimeSeriesDataFactoryEx.GetData();
var allAttributes = new List<string>()
{
TimeSeriesConstants.TOTAL_COST_CODE,
TimeSeriesConstants.TOTAL_VALUE_CODE,
TimeSeriesConstants.SOURCE_CODE
};
var latestList = new List<TimeSeries>();
var allValues = data.Values.ToList();
#region HOW DO I DO THIS USING LINQ?
bool found = false;
foreach (var attribute in allAttributes)
{
found = false;
foreach (var tsData in allValues)
{
foreach (var ts in tsData)
{
if (ts.MetricName == attribute && !string.IsNullOrEmpty(ts.MetricValue))
{
latestList.Add(ts);
found = true;
break;
}
}
if (found)
break;
}
}
#endregion
Assert.IsTrue(latestList.Count == 3);
Assert.IsTrue(latestList.Where(x => x.MetricName == TimeSeriesConstants.TOTAL_COST_CODE).First().MetricValue == "1");
Assert.IsTrue(latestList.Where(x => x.MetricName == TimeSeriesConstants.TOTAL_VALUE_CODE).First().MetricValue == "2");
Assert.IsTrue(latestList.Where(x => x.MetricName == TimeSeriesConstants.SOURCE_CODE).First().MetricValue == "gp");
Assert.IsTrue(latestList.Where(x => x.MetricName == TimeSeriesConstants.SOURCE_CODE).First().Quarter == DateTime.Today.AddMonths(-3));
}
internal class TimeSeriesDataFactoryEx
{
public static SortedDictionary<DateTime?,List<TimeSeries>> GetData()
{
return new SortedDictionary<DateTime?, List<TimeSeries>>(new DateComparer())
{
{
DateTime.Today, new List<TimeSeries>()
{
new TimeSeries()
{
Quarter = DateTime.Today,
MetricValue = "1",
MetricName = TimeSeriesConstants.TOTAL_COST_CODE
},
new TimeSeries()
{
Quarter = DateTime.Today,
MetricValue = "2",
MetricName = TimeSeriesConstants.TOTAL_VALUE_CODE
},
new TimeSeries()
{
Quarter = DateTime.Today,
MetricValue = "",
MetricName = TimeSeriesConstants.SOURCE_CODE
}
}
},
{
DateTime.Today.AddMonths(-3), new List<TimeSeries>()
{
new TimeSeries()
{
Quarter = DateTime.Today.AddMonths(-3),
MetricValue = "3",
MetricName = TimeSeriesConstants.TOTAL_COST_CODE
},
new TimeSeries()
{
Quarter = DateTime.Today.AddMonths(-3),
MetricValue = "4",
MetricName = TimeSeriesConstants.TOTAL_VALUE_CODE
},
new TimeSeries()
{
Quarter = DateTime.Today.AddMonths(-3),
MetricValue = "gp",
MetricName =TimeSeriesConstants.SOURCE_CODE
}
}
}
};
}
}
So, assuming I understand your question right, say you have a dictionary like so:
{ Key = "1/1/1900", Value = List Of Objects, of which each has a DateTimeProperty }
...
{ Key = "1/4/1900", Value = List Of Objects, of which each has a DateTimeProperty }
And you are looking to find a set of objects from your dictionary, where it's the latest by time of each key, then you can do this pretty simply with linq:
var latestItems = data.SelectMany(kvp =>
kvp.Value.OrderByDescending(value => value.Quarter).Take(1)
);
This query finds the most recent object in each bucket and then returns that as a single set (not an enumerable of enumerables). You can change the selector inside the SelectMany to find elements in each set as much as you want, as long as you return an IEnumerable from that callback.