c# Linq List - handle null - c#

which in the second list im trying to create a relationship, however if it cant a match, how do I ignore and not add an item?
var clientData = File.ReadAllLines(txtClients.Text)
.Skip(1)
.Select(x => x.Split(','))
.Select(x => new Client()
{
ClientTempId = x[0],
Email = x[1],
FirstName = x[2],
LastName = x[3],
AccountId = accountId
});
var orderData = File.ReadAllLines(txtOrders.Text)
.Skip(1)
.Select(x => x.Split(','))
.Select(x => new Order()
{
OrderTempId = x[0],
ClientId = clientData.FirstOrDefault(c=>c.ClientTempId == x[1]).Id ==string.Empty?"Error here!!":x[1],
//How do I handle errors, if client does not exist, or row is in wrong format? dont want to break code just want a list or issues
Name = x[3],
AccountId = accountId
});

You can return null instead and then filter those out:
var orderData = File.ReadAllLines(txtOrders.Text)
.Skip(1)
.Select(x => x.Split(','))
.Select(x =>
{
// do your check here, and return null
if (clientData.FirstOrDefault(c => c.ClientTempId == x[1]) == null)
return null;
// otherwise return the normal Order object
return new Order()
{
OrderTempId = x[0],
ClientId = x[1],
Name = x[3],
AccountId = accountId
};
})
// then filter out null values
.Where(x => x != null);
Once that is covered, as EZI pointed out in the comments, your actual check is quite expensive. You can make it more efficient by turning your clientData into a dictionary:
var clientDataDictionary = clientData.ToDictionary(c => c.ClientTempId);
Then, you can do the lookup above in constant time:
if (clientDataDictionary.ContainsKey(x[1]))
return null;

Related

Does not display data in linq C#

I have Linq which counts the goods, the problem is that the names that I pass, they do not work
ProductName, CompanyName, CustomerName,
Maybe there is a error in Linq?
It produces many anonymous methods that have these fields, but after ToList() everything does not work
public async Task<IEnumerable<SalesReportItem>> GetReportData(DateTime dateStart, DateTime dateEnd)
{
dateStart = new DateTime(2000, 1, 1);
var context = await _contextFactory.CreateDbContextAsync();
var queryable = context.SalesTransactionRecords.Join(
context.Products,
salesTransactionRecords => salesTransactionRecords.ProductId,
products => products.Id,
(salesTransactionRecords, products) =>
new
{
salesTransactionRecords,
products
})
.Join(context.Companies,
combinedEntry => combinedEntry.salesTransactionRecords.CompanyId,
company => company.Id,
(combinedEntry, company) => new
{
combinedEntry,
company
})
.Join(context.VendorCustomers,
combinedEntryAgain => combinedEntryAgain.combinedEntry.salesTransactionRecords.CustomerId,
vendorCustomer => vendorCustomer.Id,
(combinedEntryAgain, vendorCustomer) => new
{
CompanyName = combinedEntryAgain.company.Name,
CustomerName = vendorCustomer.Name,
ProductId = combinedEntryAgain.combinedEntry.products.Id,
ProductName = combinedEntryAgain.combinedEntry.products.Name,
combinedEntryAgain.combinedEntry.salesTransactionRecords.MovementType,
combinedEntryAgain.combinedEntry.salesTransactionRecords.Period,
combinedEntryAgain.combinedEntry.salesTransactionRecords.Quantity,
combinedEntryAgain.combinedEntry.salesTransactionRecords.Amount,
}).Where(x => x.Period >= dateStart && x.Period <= dateEnd)
.GroupBy(combinedEntryAgain => new
{
combinedEntryAgain.ProductId,
combinedEntryAgain.ProductName,
combinedEntryAgain.CompanyName,
combinedEntryAgain.CustomerName,
}
).Select(x => new SalesReportItem
{
ProductId = x.Key.ProductId,
Quantity = x.Sum(a => a.Quantity),
Amount = x.Sum(x => (x.MovementType == TableMovementType.Income ? x.Amount : -(x.Amount)))
});
var items = await queryable.ToListAsync();
return _mapper.Map<IEnumerable<SalesReportItem>>(items);
}
my mistake was that I did not specify the fields in the select, otherwise everything is buzzing, the upper code is working
Select(x => new SalesReportItem
{
ProductId = x.Key.ProductId,
ProductName = x.Key.ProductName,
CompanyName = x.Key.CompanyName,
CustomerName = x.Key.CustomerName,
Quantity = x.Sum(x => (x.MovementType == TableMovementType.Income ? x.Quantity : - x.Quantity)),
Amount = x.Sum(x => (x.MovementType == TableMovementType.Income? x.Amount: - x.Amount))
});
Thanks for the help
Hans Kesting

Is there a way to query a temporal table to see if any history exists?

My EF Core 6 application is using SQL Temporal tables.
I have an existing query that returns a number of rows based on a filter:
var results = Context.MyTable
.Where(cn => cn.Name == "Fred")
.Skip(5)
.Take(10)
.Select(x=>new { FirstName = x=>x.Name, Dob = x=>x.Dob });
What I want to do, is add a property in the response that shows if there is any history for each row in the query, so I can return it to the UI, which will show paging controls for stepping through the history.
var results = Context.MyTable
.Where(cn => cn.Name == "Fred")
.Skip(5)
.Take(10)
.Select(x=>new { FirstName = x=>x.Name, Dob = x=>x.Dob, HasHistory = x.???? });
Try the following:
var results = Context.MyTable
.Where(cn => cn.Name == "Fred")
.Skip(5)
.Take(10)
.Select(x => new
{
FirstName = x.Name,
Dob = x.Dob,
HasHistory = Context.MyTable.TemporalAll().Count(h => h.Id == x.Id) > 1
});

How to sort result based on hit score in NEST elastic search with Highlights

I am using NEST(c#) to communicate with Elasticsearch, version 1.7.3
I am passing a string and trying to multi match fields in that string.
I am getting the Highlights to figure how many fields matched in the string.
Returing the results from the Hits.Select.
But the issue is, sometimes the most matched fields in the Highlights does not appear at the top of the list from Hits.Select.
Anything to set this right??
var result = this.client.Search<PInfo>(s => s
.Take(20)
.TrackScores(true)
.Query(q => q
.MultiMatch(m => m
.Type(TextQueryType.CrossFields)
.OnFieldsWithBoost(b => b
.Add(f => f.A, 1.0)
.Add(f => f.B, 1.0)
.Add(f => f.C, 1.0)
)
.Operator(Operator.Or)
.Query(text)
))
.Highlight( h => h
//.PreTags("<b>")
//.PostTags("</b>")
.OnFields(
fk => fk.OnField( a => a.A),
fk => fk.OnField( a => a.B),
fk => fk.OnField( a => a.C)
)
)
.Sort(sort => sort.OnField("_score").Descending())
);
string value = result.ConnectionStatus.ToString();
return result
.Hits
.Select(c => new PInfo
{
Id = c.Source.Id,
A = c.Source.A,
B = c.Source.B,
C = c.Source.C,
IndexedOn = c.Source.IndexedOn,
Highlights = c.Highlights // returning the highlights too from here
})
.ToList();
This should get the desired result.
return result
.Hits
.Select(c => new PatientInfo
{
Id = c.Source.Id,
Patient_Num = c.Source.Patient_Num,
First_Name = c.Source.First_Name,
Last_Name = c.Source.Last_Name,
Full_Name = c.Source.Full_Name,
Address = c.Source.Address,
City = c.Source.City,
State = c.Source.State,
Zip = c.Source.Zip,
Relation_Code = c.Source.Relation_Code,
DOB = c.Source.DOB,
Sex = c.Source.Sex,
IndexedOn = c.Source.IndexedOn,
Highlights = c.Highlights
})
.OrderByDescending(t => t.Highlights.Count).ToList();

Select Last non null-able item per product?

Let's say I have,
class Product
{
public int Id {get; set;}
public string Name {get; set;}
public int Order {get; set;}
}
and my data have,
products[0] = new Product { Id = 1, Name = "P1", Order = 1 };
products[1] = new Product { Id = 1, Name = "P2", Order = 2 };
products[2] = new Product { Id = 1, Name = null, Order = 3 };
products[3] = new Product { Id = 2, Name = "P3", Order = 4 };
products[4] = new Product { Id = 2, Name = null, Order = 5 };
products[5] = new Product { Id = 2, Name = null, Order = 6 };
What I need is the last(order by Order desc) non-nullable value of Name per Product.Id. So my final output will look like,
items[0] = new { Id = 1, Name = "P2"};
items[1] = new { Id = 2, Name = "P3"};
If Id=1, I have 3 Names (P1, P2, null) and non-nullable Names (P1, P2) but last one is P3.
This should get the last products in order.
var lastOrders = products
.Where(x => x.Name != null) // Remove inapplicable data
.OrderBy(x => x.Order) // Order by the Order
.GroupBy(x => x.Id) // Group the sorted Products
.Select(x => x.Last()); // Get the last products in the groups
var result = products
.GroupBy(p => p.Id)
.Select(g => g.OrderBy(x => x.Order).Last(x => x.Name != null));
this will give you your desired output:
products.GroupBy(p => p.Id)
.Select(g => g.OrderByDescending(gg => gg.Name)
.Where(gg => gg.Name != null)
.Select(gg => new { gg.Id, gg.Name })
.First());
The task can be solved using the following Linq statement.
var Result = products.OrderBy().Where( null != iProduct.Name ).First();
This requires products to contain at least one item where Name is null, otherwise an Exception will be thrown. Alternatively,
var Result = products.OrderBy().Where( null != iProduct.Name ).FirstOrDefault();
will return null if products contains no such item.
Try with :
var expectedProduct =products.Where(p => p.Id != null).OrderByDescending(p => p.Order).GroupBy(p => p.Id).Last()

Getting duplicate data based on dynamic key

I have a list of Person objects:
List<PersonData> AllPersons
From this list I want all those person objects that are duplicated based on a certain property.
Example, this code give all the duplicates based on the Id
var duplicateKeys = AllPersons.GroupBy(p => p.Id).Select(g => new { g.Key, Count = g.Count() }).Where(x => x.Count > 1).ToList().Select(d => d.Key);
duplicates = AllPersons.Where(p => duplicateKeys.Contains(p.Id)).ToList();
Can the part p.Id be dynamic?
Meaning if the user specifies the unique column in a config file and it's read like so:
string uniqueColumn = "FirstName";
How can the query be composed to add that functionality?
Regards.
You can use Reflection to achieve that:
List<PersonData> AllPersons = new List<PersonData>()
{
new PersonData { Id = 1, FirstName = "Tom" },
new PersonData { Id = 2, FirstName = "Jon" },
new PersonData { Id = 3, FirstName = "Tom" }
};
string uniqueColumn = "FirstName";
var prop = typeof(PersonData).GetProperty(uniqueColumn);
var duplicateKeys = AllPersons.GroupBy(p => prop.GetValue(p, null))
.Select(g => new { g.Key, Count = g.Count() })
.Where(x => x.Count > 1)
.Select(d => d.Key)
.ToList();
var duplicates = AllPersons.Where(p => duplicateKeys.Contains(prop.GetValue(p, null))).ToList();
duplicates have 2 elements with FirstName == "Tom" after query execution.
You might want to look into Dynamic LINQ or PredicateBuilder.

Categories