Merge data from two arrays or something else - c#

How to combine Id from the list I get from file /test.json and id from list ourOrders[i].id?
Or if there is another way?
private RegionModel FilterByOurOrders(RegionModel region, List<OurOrderModel> ourOrders, MarketSettings market, bool byOurOrders)
{
var result = new RegionModel
{
updatedTs = region.updatedTs,
orders = new List<OrderModel>(region.orders.Count)
};
var json = File.ReadAllText("/test.json");
var otherBotOrders = JsonSerializer.Deserialize<OrdersTimesModel>(json);
OtherBotOrders = new Dictionary<string, OrderTimesInfoModel>();
foreach (var otherBotOrder in otherBotOrders.OrdersTimesInfo)
{
//OtherBotOrders.Add(otherBotOrder.Id, otherBotOrder);
BotController.WriteLine($"{otherBotOrder.Id}"); //Output ID orders to the console works
}
foreach (var order in region.orders)
{
if (ConvertToDecimal(order.price) < 1 || !byOurOrders)
{
int i = 0;
var isOurOrder = false;
while (i < ourOrders.Count && !isOurOrder)
{
if (ourOrders[i].id.Equals(order.id, StringComparison.InvariantCultureIgnoreCase))
{
isOurOrder = true;
}
++i;
}
if (!isOurOrder)
{
result.orders.Add(order);
}
}
}
return result;
}
OrdersTimesModel Looks like that:
public class OrdersTimesModel
{
public List<OrderTimesInfoModel> OrdersTimesInfo { get; set; }
}
test.json:
{"OrdersTimesInfo":[{"Id":"1"},{"Id":"2"}]}
Added:
I'll try to clarify the question:
There are three lists with ID:
First (all orders): region.orders, as order.id
Second (our orders): ourOrders, as ourOrders[i].id in a while loop
Third (our orders 2): from the /test.json file, as an array {"Orders":[{"Id":"12345..."...},{"Id":"12345..." ...}...]}
There is a foreach in which there is a while, where the First (all orders) list and the Second (our orders) list are compared. If the id's match, then these are our orders: isOurOrder = true;
Accordingly, those orders that isOurOrder = false; will be added to the result: result.orders.Add(order)
I need:
So that if (ourOrders[i].id.Equals(order.id, StringComparison.InvariantCultureIgnoreCase)) would include more Id's from the Third (our orders 2) list.
Or any other way to do it?

You should be able to completely avoid writing loops if you use LINQ (there will be loops running in the background, but it's way easier to read)
You can access some documentation here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/introduction-to-linq-queries
and you have some pretty cool extension methods for arrays: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=net-6.0 (these are great to get your code easy to read)
Solution
unsing System.Linq;
private RegionModel FilterByOurOrders(RegionModel region, List<OurOrderModel> ourOrders, MarketSettings market, bool byOurOrders)
{
var result = new RegionModel
{
updatedTs = region.updatedTs,
orders = new List<OrderModel>(region.orders.Count)
};
var json = File.ReadAllText("/test.json");
var otherBotOrders = JsonSerializer.Deserialize<OrdersTimesModel>(json);
// This line should get you an array containing
// JUST the ids in the JSON file
var idsFromJsonFile = otherBotOrders.Select(x => x.Id);
// Here you'll get an array with the ids for your orders
var idsFromOurOrders = ourOrders.Select(x => x.id);
// Union will only take unique values,
// so you avoid repetition.
var mergedArrays = idsFromJsonFile.Union(idsFromOurOrders);
// Now we just need to query the region orders
// We'll get every element that has an id contained in the arrays we created earlier
var filteredRegionOrders = region.orders.Where(x => !mergedArrays.Contains(x.id));
result.orders.AddRange(filteredRegionOrders );
return result;
}
You can add conditions to any of those actions (like checking for order price or the boolean flag you get as a parameter), and of course you can do it without assigning so many variables, I did it that way just to make it easier to explain.

Related

Fastest equivalent of comparing all elements of array

What is the fastest equivalent in C#/LINQ to compare all combination of elements of an array like so and add them to a bucket if they are not in a bucket. AKA. How could I optimize this piece of code in C#.
// pseudocode
List<T> elements = { .... }
HashSet<T> bucket = {}
foreach (T element in elements)
foreach (var myelemenet in elements.Where(e => e.id != element.id))
{
if (!element.notInTheList)
{
_elementIsTheSame = element.Equals(myelement);
if (_elementIsTheSame)
{
// append element to a bucket
if (!elementIsInTheBucket(bucket, element))
{
element.notInTheList = true;
addToBucket(bucket, element);
}
}
}
}
}
// takes about 150ms on a fast workstation with only 300 elements in the LIST!
The final order of the elements in the bucket is important
elements.GroupBy(x=>x).SelectMany(x=>x);
https://dotnetfiddle.net/yZ9JDp
This works because GroupBy preserves order.
Note that this puts the first element of each equivalence class first. Your code puts the first element last, and skips classes with just a single element.
Skipping the classes with just a single element can be done with a where before the SelectMany.
elements.GroupBy(x=>x).Where(x=>x.Skip(1).Any()).SelectMany(x=>x);
Getting the first element last is a bit more tricky, but I suspect it's a bug in your code so I will not try to write it out.
Depending on how you use the result you might want to throw a ToList() at the end.
It sounds like you're effectively after DistinctBy? Which can be simulated with something like:
var list = new List<MainType>();
var known = new HashSet<PropertyType>();
foreach (var item in source)
{
if (known.Add(item.TheProperty))
list.Add(item);
}
You now have a list of the items taking the first only when there are duplicates via the selected property, preserving order.
If the intent is to find the fastest solution, as the StackOverflow title suggests, then I would consider using CSharp's Parallel.ForEach to perform a map and reduce.
For example:
var resultsCache = new IRecord[_allRecords.Length];
var resultsCount = 0;
var parallelOptions = new ParallelOptions
{
MaxDegreeOfParallelism = 1 // use an appropriate value
};
Parallel.ForEach(
_allRecords,
parallelOptions,
// Part1: initialize thread local storage
() => { return new FilterMapReduceState(); },
// Part2: define task to perform
(record, parallelLoopState, index, results) =>
{
if (_abortFilterOperation)
{
parallelLoopState.Break();
}
if (strategy.CanKeep(record))
{
resultsCache[index] = record;
results.Count++;
}
else
{
resultsCache[index] = null;
}
return results;
},
// Part3: merge the results
(results) =>
{
Interlocked.Add(ref resultsCount, results.Count);
}
);
where
class FilterMapReduceState
{
public FilterMapReduceState()
{
this.Count = 0;
}
/// <summary>
/// Represents the number of records that meet the search criteria.
/// </summary>
internal int Count { get; set; }
}
As I understand from what you did you need is these pieces.
var multipleIds=elements.GroupBy(x => x.id)
.Where(g => g.Count() > 1)
.Select(y => y.Key);
var distinctIds=elements.Select(x=>x.id).Distinct();
var distinctElements=elements.Where(x=>distinctIds.Contains(x.id));
}

How to combine two c# objects using Linq

I am trying to combine two LIKE objects together and remove duplicates.
Tried this
This didn't work
Here is my object [simple]
public class LabelItem
{
public string LabelName { get; set; }
public string LabelValue { get; set; }
}
my data call returns the same object type
public static List<LabelItem> ReturnControlLabelList(Enums.LanguageType languageType, string labelList = "")
I pass this to the method
string[] LABELLIST = new string[] { "foxLabel", "commonLabel" };
var helper = new LabelHelper(, LABELLIST);
this is where I get null
public LabelHelper(Enums.LanguageType languageType, string[] labelListName)
{
if (labelListName != null)
{
List<LabelItem> labels = new List<LabelItem>();
this.LabelList = new List<LabelItem>();
foreach (var name in labelListName)
{
labels = DBCommon.ReturnControlLabelList(languageType, name);
this.LabelList.Concat(labels).Distinct().ToList();
}
}
else
{
this.LabelList = null;
}
}
public List<LabelItem> LabelList { get; private set; }
The concat is not working. I keep getting count 0 for labels and I can see the returns come back with 275 and 125 in the for loop.
Thanks in advance for the help.
Still having an issue
I want to use the suggestion from below but am still struggling.
The string[] passed in will get two lists of labelitems that are not unique when joined together in the loop. I need the distinct of the multiple lists returned in this.LabelList.
I got it to work with this but...I'm sure it's crazy inefficient.
Thanks for the help.
this.LabelList = new List<LabelItem>();
foreach (var name in labelListName)
{
var ret = DBCommon.ReturnControlLabelList(languageType, name);
this.LabelList = this.LabelList.Concat(ret).Distinct().ToList();
}
var distinctList = this.LabelList.GroupBy(x => new { x.LabelName, x.LabelValue })
.Select(x => x.FirstOrDefault());
this.LabelList = new List<LabelItem>();
foreach (var item in distinctList)
{
this.LabelList.Add(item);
Debug.WriteLine(item.LabelName + ' ' + item.LabelValue);
}
}
this.LabelList.Concat(labels).Distinct().ToList(); without assigning it to something doesn't make much sense. LINQ query does not modify the source collection, it returns a new one, so you'd have to assign it back to this.LabelList if you want it to get updated:
this.LabelList = this.LabelList.Concat(labels).Distinct().ToList();
You should be aware, that it's highly inefficient solution, and you should go with something based on SelectMany:
this.LabelList
= labelListName.SelectMany(name => DBCommon.ReturnControlLabelList(languageType, name)
.Distinct()
.ToList();
Concat and most other linq methods return an IEnumerable which you then need to do something with. It will not change your existing list so you need to just assign it with:
this.LabelList = this.LabelList.Concat(labels).Distinct().ToList();

Neo4j: How to return multiple paths from different starting nodes

I have a question similar to
this question but I am using the c# with the neo4jClient instead of the Java.
I can get the parent path of a given node with the following code but it becomes a performance bottle neck when trying to find the parent path of many nodes. What I would like is a way to call the graph database once with a list of node keys and get back a list of parent paths. So that I can return a dictionary of results instead of a single list.
Any help accomplishing this would be greatly appreciated! Also if my original cypher query can be improved I'm open to that as well.
public IEnumerable<IGenericEntity> GetPath(string entityCompositeKey, GraphRelationship relationship)
{
var entity = new GenericEntity();
entity.setCompositeKey(entityCompositeKey);
var pathToRoot = new List<GenericEntity>(){ entity };
var query = new CypherFluentQuery(graphClient)
.Match("p = (current)-[r:" + relationship.Name + "*0..]->()")
.Where((IGenericEntity current) => current.CompositeKey == entityCompositeKey)
.Return(() => Return.As<IEnumerable<GenericEntity>>("nodes(p)"))
.OrderByDescending("length(p)")
.Limit(10);
var queryText = query.Query.QueryText;
var paramText = query.Query.QueryParameters;
if (query.Results != null)
{
var graphResults = query.Results.FirstOrDefault();
if (graphResults != null && graphResults.ToList().Count > 0)
{
pathToRoot = graphResults.ToList();
}
}
return pathToRoot;
}
There are a few things I'm not sure of - and it's most likely how my test DB is setup.
To answer the initial question of how to pass in multiple start nodes - that's probably best approached using the UNWIND operator, which in Neo4jClient is used like so:
var enumerable = new string[] { "a", "b" }
client.Unwind(enumerable, "item"). /*The rest*/
Obvs, if you place that in the top of your current query you'll get a monster set of nodes back, and you won't know which Root entity refers to which, soo... let's do some projecting...
To project, we must have something to project into:
public class Result {
public GenericEntity Root { get; set; }
public List<GenericEntity> Nodes { get; set; }
public int Length { get; set; }
}
This will contain the Root node, and the path to it, now to fill.
public IEnumerable<Result> GetPath(IEnumerable<string> rootKeys, GraphRelationship relationship)
{
var query = new CypherFluentQuery(Client)
.Unwind(rootKeys, "entityRootKey")
.Match(string.Format("p = (root)-[r:{0}*0..]->()", relationship.Name))
.Where("root.CompositeKey = entityRootKey")
.With("{Root:root, Nodes: nodes(p), Length: length(p)} as res")
.Return((res) => res.As<Result>())
.OrderByDescending("res.Length")
.Limit(10);
var results = query.Results;
return results;
}
I'm not using .Where with a parameter creating Func<T> this is because the parameter is created in the .Unwind statement.
Usage wise - something like this:
var res = GetPath(new[] {"a", "b"}, new GraphRelationship {Name = "RELATED"});
foreach (var result in res)
{
Console.WriteLine($"{result.Root.CompositeKey} => {result.Length}");
foreach (var node in result.Nodes)
Console.WriteLine($"\t{node.CompositeKey}");
}

Why is RemoveAll(x => x.Condition) removing all my records?

I'm working on creating a filter for a collection of employees. In order to do this I initially fetch a raw collection of all employees. I clone this list so I can iterate over the original list but remove items from the second list.
For each filter I have, I build a collection of employee ids that pass the filter. Having gone through all filters I then attempt to remove everything that isn't contained in any of these lists from the cloned list.
However for some reason, whenever I attempt to do this using .RemoveAll(), all records seemed to be removed and I can't figure out why.
Here is a stripped down version of the method I'm using, with only 1 filter applied:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
List<int> filteredEmployeeIds = employeeIds.Clone();
// Now filter the results based on which checkboxes are ticked
foreach (var employeeId in employeeIds)
{
// 3rd party API used to get values - please ignore for this example
Member m = new Member(employeeId);
if (m.IsInGroup("Employees"))
{
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
// Filter by brand
List<int> filteredEmployeeIdsByBrand = new List<int>();
if (brandId != null)
{
if (brandId == memberBrandId)
filteredEmployeeIdsByBrand.Add(m.Id);
var setToRemove = new HashSet<int>(filteredEmployeeIdsByBrand);
filteredEmployeeIds.RemoveAll(x => !setToRemove.Contains(x));
}
}
}
return filteredEmployeeIds;
}
As you can see, I'm basically attempting to remove all records from the cloned record set, wherever the id doesn't match in the second collection. However for some reason every record seems to be getting removed.
Anybody know why?
P.S: Just to clarify, I have put in logging to check the values throughout the process and there are records appearing in the second list, however for whatever reason they're not getting matched in the RemoveAll()
Thanks
Ok only minutes after posting this I realised what I did wrong: The scoping is incorrect. What it should've been was like so:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
List<int> filteredEmployeeIds = employeeIds.Clone();
List<int> filteredEmployeeIdsByBrand = new List<int>();
// Now filter the results based on which checkboxes are ticked
foreach (var employeeId in employeeIds)
{
Member m = new Member(employeeId);
if (m.IsInGroup("Employees"))
{
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
// Filter by brand
if (brandId != null)
{
if (brandId == memberBrandId)
filteredEmployeeIdsByBrand.Add(m.Id);
}
}
}
var setToRemove = new HashSet<int>(filteredEmployeeIdsByBrand);
filteredEmployeeIds.RemoveAll(x => !setToRemove.Contains(x));
return filteredEmployeeIds;
}
Essentially the removal of entries needed to be done outside the loop of the employee ids :-)
I know that you said your example was stripped down, so maybe this wouldn't suit, but could you do something like the following:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
return employeeIds.Where(e => MemberIsEmployeeWithBrand(e, brandId)).ToList();
}
private bool MemberIsEmployeeWithBrand(int employeeId, int? brandId)
{
Member m = new Member(employeeId);
if (!m.IsInGroup("Employees"))
{
return false;
}
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
return brandId == memberBrandId;
}
I've just done that off the top of my head, not tested, but if all you need to do is filter the employee ids, then maybe you don't need to clone the original list, just use the Where function to do the filtering on it directly??
Please someone let me know if i've done something blindingly stupid!!

Cannot implicitly convert type '.List<AnonymousType#1>' to '.List<WebApplication2.Customer>'

In the following code that returns a list:
public List<Customer> GeAllCust()
{
var results = db.Customers
.Select(x => new { x.CustName, x.CustEmail, x.CustAddress, x.CustContactNo })
.ToList()
return results;
}
I get an error reporting that C# can't convert the list:
Error: Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Collections.Generic.List<WebApplication2.Customer>
Why is that?
Here's a screenshot showing some additional information that Visual Studio provides in a tooltip for the error:
Is it right way to return some columns instead of whole table....?
public object GeAllCust()
{
var results = db.Customers.Select(x => new { x.CustName, x.CustEmail, x.CustAddress, x.CustContactNo }).ToList();
return results;
}
When you look the code:
x => new { ... }
This creates a new anonymous type. If you don't need to pull back only a particular set of columns, you can just do the following:
return db.Customers.ToList();
This assumes that Customers is an IEnumerable<Customer>, which should match up with what you are trying to return.
Edit
You have noted that you only want to return a certain subset of columns. If you want any sort of compiler help when coding this, you need to make a custom class to hold the values:
public class CustomerMinInfo
{
public string Name { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public int? ContactNumber { get; set; }
}
Then change your function to the following:
public List<CustomerMinInfo> GetAllCust()
{
var results = db.Customers.Select(x => new CustomerMinInfo()
{
Name = x.CustName,
Email = x.Email,
Address = x.Address,
ContactNumber = x.CustContactNo
})
.ToList();
return results;
}
This will work, however, you will lose all relationship to the database context. This means if you update the returned values, it will not stick it back into the database.
Also, just to repeat my comment, returning more columns (with the exception of byte arrays) does not necessarily mean longer execution time. Returning a lot of rows means more execution time. Your function is returning every single customer in the database, which when your system grows, will start to hang your program, even with the reduced amount of columns.
You are selecting to an anonymous type, which is not a Customer.
If you want to do (sort of) this, you can write it like this:
return db.Customers.Select(x => new Customer { Name = x.CustName, Email = x.CustEmail, Address = x.CustAddress, ContactNo = x.ContactNo }).ToList();
This assumes the properties on your Customer object are what I called them.
** EDIT ** Per your comment,
If you want to return a subset of the table, you can do one of two things:
Return the translated form of Customer as I specified above, or:
Create a new class for your business layer that only has only those four fields, and change your method to return a List<ShrunkenCustomer> (assuming ShunkenCustomer is the name that you choose for your new class.)
GetAllCust() is supposed to return a List of Customer, Select New will create a list of Anonymous Types, you need to return a list of Customer from your query.
try:
var results = db.Customers.Select( new Customer{CustName = x.CustName}).ToList(); //include other fields
I guess Customer is a class you have defined yourself?
The my suggestion would be to do something like the following:
var results = db.Customers.Select(x => new Customer(x.Custname, x.CustEmail, x.CustAddress, x.CustContactNo)).ToList();
The reason is that you are trying to return a list of Customer but the results from your link is an anonymous class containing those four values.
This would of course require that you have a constructor that takes those four values.
Basically whatever u got in var type, loop on that and store it in list<> object then loop and achieve ur target.Here I m posting code for Master details.
List obj = new List();
var orderlist = (from a in db.Order_Master
join b in db.UserAccounts on a.User_Id equals b.Id into abc
from b in abc.DefaultIfEmpty()
select new
{
Order_Id = a.Order_Id,
User_Name = b.FirstName,
Order_Date = a.Order_Date,
Tot_Qty = a.Tot_Qty,
Tot_Price = a.Tot_Price,
Order_Status = a.Order_Status,
Payment_Mode = a.Payment_Mode,
Address_Id = a.Address_Id
});
List<MasterOrder> ob = new List<MasterOrder>();
foreach (var item in orderlist)
{
MasterOrder clr = new MasterOrder();
clr.Order_Id = item.Order_Id;
clr.User_Name = item.User_Name;
clr.Order_Date = item.Order_Date;
clr.Tot_Qty = item.Tot_Qty;
clr.Tot_Price = item.Tot_Price;
clr.Order_Status = item.Order_Status;
clr.Payment_Mode = item.Payment_Mode;
clr.Address_Id = item.Address_Id;
ob.Add(clr);
}
using(ecom_storeEntities en=new ecom_storeEntities())
{
var Masterlist = en.Order_Master.OrderByDescending(a => a.Order_Id).ToList();
foreach (var i in ob)
{
var Child = en.Order_Child.Where(a => a.Order_Id==i.Order_Id).ToList();
obj.Add(new OrderMasterChild
{
Master = i,
Childs = Child
});
}
}

Categories

Resources