Order complex object via LINQ - linq

I have a complex object:
+ Client
+ Products
+ Types
| TypeId
| ManagerCode
| TypeName
....
A single client will be associated with a collection of products they have bought, and each product will have a collection of types associated with it.
I need to sort the 'Types' on two properties - ManagerCode and TypeName.
eg: client.Products.ForEach(o => o.Types.OrderBy(o1 => o1.BaseType.ManagerCode));
Well, when I do this, the list doesn't actually order once returned to the front end. It maintains the original sort order. What am I doing wrong?

OrderBy doesn't replace the original collection; it returns a newly-sorted collection.
Assuming Types is a list, you need
client.Products.ForEach(o =>
o.Types = o.Types.OrderBy(o1 => o1.BaseType.ManagerCode).ToList());

An alternative solution would be to use the Sort method.
client.Products.ForEach(o => o.Types.Sort(
(x, y) => string.Compare(x.BaseType.ManagerCode, y.BaseType.ManagerCode));

Related

TYPO3 extbase access sorting

when using the fluid debug array I see a nested array lige this:
building
[+]floor
[+]room
When clicking the + the sub array is expanded sorted by UID and not by the "sorting" as I have specified in my repository.
protected $defaultOrderings = array(
'sorting' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
);
Can I either access the sorting value somehow?
Or can I somehow force TYPO3 to sort its own array after sorting?
The sorting field should not be manually set.
If you want to sort the entries please use the DataHandler like the Typo3 Backend.
Here is a solution:
TYPO3 CommandController: How to set table field "sorting" of Extbase Object?
By "clicking the +", you mean that the subobjects aren't sorted the way you specified in the TCA?
The sorting is lost on the subobjects because Extbases PersistenceRepository by default only sorts by the order specified in the object itself. But thats no biggie, you just have to specify to order by the subproperty, either with the defaultOrderings property or when building the query:
class FloorRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
// Order by BE sorting
protected $defaultOrderings = array(
'sorting' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
'room.sorting' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
);
...
}
This should give you the Rooms of a Floor, Ordered by the sorting of the Floors, and in each Floor, the Rooms sorted by their sorting.
You can access the sorting property by defining it as integer in your model and creating the related getter (setter if needed)
protected $sorting

Laravel attach pivot to table with multiple values

Background
I'm creating a database revolving around food allergies and I have a many to many relationship between foods and allergies. There is also a pivot value called severity which has a numerical number representing the severity of the allergy for that food item.
This link table looks like this;
food_id|allergy_id|severity
-------|----------|--------
1 | 1 | 3
1 | 4 | 1
2 | 2 | 1
The problem
When trying to update the link table with Eloquent (where $allergy_ids is an array)
$food->allergies()->attach($allergy_ids);
How would I go about adding multiple values to this pivot table at once along with the pivot values?
I can add all the allergy_id's for a particular food item in one go using the above line, but how can I also add in the severity column at the same time with an array of various severity values? Maybe something like
$food->allergies()->attach($allergy_ids, $severity_ids);
Edit: There could be between 0-20 allergies for a specific food item, and a severity rating from 0-4 for each allergy, if this helps at all.
You can.
From this example in Docs (4.2, 5.0):
$user->roles()->sync(array(1 => array('expires' => true)));
Hardcoded version for the first two rows:
$food = Food::find(1);
$food->allergies()->sync([1 => ['severity' => 3], 4 => ['severity' => 1]]);
Dynamically, with your arrays $allergy_ids and $severities in a compatible state (size and sort), you shall prepare your sync data before. Something like:
$sync_data = [];
for($i = 0; $i < count($allergy_ids); $i++))
$sync_data[$allergy_ids[$i]] = ['severity' => $severities[$i]];
$food->allergies()->sync($sync_data);
You can't do it like you' like so I suggest a simple loop:
foreach ($allergy_ids as $key => $id)
{
$food->allergies()->attach($id, array_get($severity_ids, $key));
// should you need a sensible default pass it as a 3rd parameter to the array_get()
}
workaround
However if you wanted to attach multiple allergies with single severity level/id then you could do this:
$food->allergies()->attach($allergy_ids, array('severity' => $singleSeverityValue));
From version 5.1 of Laravel (Currently in Laravel 9.x) onwards it is possible to pass an array as a second argument with all the additional parameters that need to be saved in the intermediate table.
As you can read in the documentation
When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table:
$user->roles()->attach($roleId, ['expires' => $expires]);
For convenience, attach and detach also accept arrays of IDs as input:
$user->roles()->attach([1 => ['expires' => $expires], 2, 3]);
Then you can simply do
$food->allergies()->attach([1 => ['severity' => 3], 4 => ['severity' => 1]]);
So, on Laravel 9, passing the ids in the array worked for me. Likeso,
$user->roles()->attach([$a->id,$b->id,$c->id]); and so on.
I guess instead of passing the string. We can pass just the id or else convert the string into array.
Easiest indeed is to attach with the extra data, like so:
$retailer->paymentmethods()->attach($paymentmethod, array('currency' => $paymentmethod->currency));
change out the values for food allergy severity, but you get the hint... :-)

Is there a way to merge multiple properties and then sort via LINQ?

I'm working on a class that has URL and FileName fields. An object can either have a URL or a FileName, but can't have both at the same time.
Is there any way to merge these two fields via LINQ and then sort them? I know I can't use
OrderBy(i => item.URL).ThenBy(i => item.FileName);
because it would just sort the items via URL first and then by their respective FileNames. I need to sort it as if I'm sorting only one field.
Thank you :)
var sorted = list.OrderBy(x => x.URL + x.FileName);
You can pad the URL if desired, or do just about any other operation you need.

ActiveRecord: Find through multiple instances

Say I have the following in my controller:
#category1
#category2
and I want to find all stores associated with those two categories...
#stores = #category1.stores + #category2.stores
this does work, but unfortunately returns an unaltered Array, rather than a AR::Base Array, and as such, I can't do things like pagination, scope, etc...
It seems to me like there's a built-in way of finding through multiple instance association... isn't there?
##stores = #category1.stores + #category2.stores
#if you want to call API methods you can just add conditions with the category id
#stores = Store.find(:all, :conditions => ['category_id=?', a || b])
With ActiveRecord, whenever you're finding a set of unique model objects, calling find on that model is usually your best bet.
Then all you need to do is constrain the join table with the categories you care about.
#stores = Store.all(:joins => :categories,
:conditions => ['category_stores.category_id in (?)', [#category1.id, #category2.id]])

Checking for duplicates in a complex object using Linq or Lambda expression

I've just started learning linq and lambda expressions, and they seem to be a good fit for finding duplicates in a complex object collection, but I'm getting a little confused and hope someone can help put me back on the path to happy coding.
My object is structured like list.list.uniqueCustomerIdentifier
I need to ensure there are no duplicate uniqueCustomerIdentifier with in the entire complex object. If there are duplicates, I need to identify which are duplicated and return a list of the duplicates.
Unpack the hierarchy
Project each element to its uniqueID property
Group these ID's up
Filter the groups by groups that have more than 1 element
Project each group to the group's key (back to uniqueID)
Enumerate the query and store the result in a list.
var result =
myList
.SelectMany(x => x.InnerList)
.Select(y => y.uniqueCustomerIdentifier)
.GroupBy(id => id)
.Where(g => g.Skip(1).Any())
.Select(g => g.Key)
.ToList()
There is a linq operator Distinct( ), that allows you to filter down to a distinct set of records if you only want the ids. If you have setup your class to override equals you or have an IEqualityComparer you can directly call the Distinct extension method to return the unique results from the list. As an added bonus you can also use the Union and Intersect methods to merge or filter between two lists.
Another option would be to group by the id and then select the first element.
var results = from item in list
group item by item.id into g
select g.First();
If you want to flatten the two list hierarchies, use the SelectMany method to flatten an IEnumerable<IEnumerable<T>> into IEnumerable<T>.

Resources