setting variable name with variable at the end - perl - perl

$id = 1;
foreach (#products) {
$productid$id = param(product$id);
++$id;
}
I am trying to loop through the products array and create a new variable for each item in products for a form which uses checkboxes to select products which are for sale. I tried to use $productid$id and $productid"$id" but it returned syntax errors. Is there a way to achieve this?

I am trying to loop through the products array and create a new variable for each item in products.
While it is possible to create a new variable for each item, it's a bad idea. They have to be global, the syntax is ugly, and it's difficult to pass those variables around as a collection. Instead, use a hash or an array. In this case, since your items are indexed by number and there's no gaps an array makes the most sense.
my #product_params;
foreach my $product (#products) {
push #product_params, param($product);
}
For each $product in #products, it will add param($product) to #product_params. For example, the parameter for $products[5] is stored in $product_params[5].
Since this is a one-to-one mapping of #products to its parameters, its easier and faster to do the above with a map. A mapping runs each element of a list through a function to create a new list. $_ contains each element of #products in turn.
my #product_params = map { param($_) } #products;

Variables aren't interpolated in single quotes.
You're resetting the $id in each iteration of the loop.
Update: Your changes invalidated both the bullets above.
my $id = 1;
for my $product (#products) {
$product_id = param("product$id");
++$id;
}

Say you have four checkboxes named
product1
product2
product3
product4
Say product1 and product3 are checked.
The following will place 1 and 3 in #selected_product_ids:
my #selected_product_ids =
map { my ($id) = /^product(\d+)\z/; defined($id) && param($_) ? $id : () }
params();
If you have the list of all existing product ids in #product_ids, the following will do the same:
my #selected_product_ids =
grep { param("product$_") }
#product_ids;

Related

Laravel Mongo GroupBy And count in an efficient manner

I have an authors collection that has a many-to-many relation to a posts collection. The posts collection has a category field which can be, for example, "Suspense", "Drama", "Action", etc.
I need to go through my entire posts collection and group by the category fields and also have the associated counts.
This is what I have so far:
$authors = Author::where('active', true)->get();
foreach ($authors as $author){
foreach ($author->posts()->groupBy('category')->get() as $data){
// Do some logic
}
}
Basically the output I am expecting is an array with category as keys and the counts as a value. So if I do $a['Drama'] it gives me count of how many times that author wrote a post with that category.
I can probably figure it out by working in my loop logic above but it does not look very efficient. Should I look at aggregation? If so can someone get me started?
Try this:
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
$authors = DB::collection('authors')
->where('active', true)
->get();
// Laravel >= 5.3 would return collection instead of plain array
$authors = is_array($authors) ? Collection::make($authors) : $authors;
$authors->map(function ($author) {
return $author
->posts()
->groupBy('category')
->get()
->map(function ($collection) {
return $collection->count();
});
});

Load relations only when a field in parent table is true

I have a parent table named Post which has a boolean column named is_anonymous. The Post table has a relation to Users table. I want to load this relation only when is_anonymous set to false. Is there a way I can achieve this?
The below relation gives users for all the posts.
$institute = Institute::where('inst_id', '=', $institute_id)->with(
['posts' => function ($posts) {
$posts->with(['user', 'tags']);
}]
)->orderBy('created_at', 'DESC')->skip($skip)->take(10)->get();
I ended up solving this using lazy loading. I think there is no other way I can do this. Please add answers if you find a better way.
$posts = Institute::where('inst_id', '=', $institute_id)->first()->posts()->with('tags')
->orderBy('created_at', 'DESC')->skip($skip)->take(10)->get();
foreach ($posts as $post) {
if (!$post['is_anonymous']) {
$post->load('user');
}
}
Or,
To be performance friendly, I'm currently solving this using the below query and removing the key user, as iterating over a list of 10 items and removing is always better than making 10 queries (worst case)
$posts = Institute::where('inst_id', '=', $institute_id)->first()->posts()->with(['user', 'tags'])
->orderBy('created_at', 'DESC')->skip($skip)->take(10)->get();
foreach ($posts as $post) {
if ($post['is_anonymous']) {
unset($post['user'])
}
}

Laravel 5 Eloquent with MongoDB - get array of column names from document

this is becoming frustrating beyond imagination.
I need to get the column names from a table using Eloquent ORM in Laravel 5 combined with MongoDB. I have found some examples, but none of them is working for me as they are probably made for SQL specifically. I tried this and this without success, any idea?
Thanks!
It would be best to use the raw() method in this case and use the native MongoCollection methods like find() to iterate over the collection and get the keys in the documents array:
// Returns an array of field names from a collection of User models.
$keys = DB::collection('users')->raw(function($collection)
{
$cursor = $collection->find();
$array = iterator_to_array($cursor);
$fields = array();
foreach ($array as $k=>$v) {
foreach ($v as $a=>$b) {
$fields[] = $a;
}
}
return array_values(array_unique($fields));
});
MongoDB doesn't have columns or tables - the whole point is that it's schemaless therefore there are no columns for you to get the names of.
Because every document can be different, you'd need to get all documents in your collection and build an array of unique keys that each document contains.
See this answer:
MongoDB Get names of all keys in collection

Entity Framework: A better way to update lots of table records

I am new at Entity framework, and curious what the best way would be to update all tables with records of new data. I have a method which returns a list of objects with updated records. Most of the information stays the same; just two fields will be updated.
Currently I created two ways of doing that update.
The first one is to get data from the database table and iterate from both Lists to find a match and update that match:
var previousDatafromTable= db.Widgets.ToList();
var newDataReturnedFromMethod =.......
foreach (var d in previousDatafromTable)
{
foreach (var l in newDataReturnedFromMethod )
{
if (d.id == l.id)
{
d.PositionColumn = l.PositionColumn;
d.PositionRow = l.PositionRow;
}
}
The second one is:
foreach (var item in newDataReturnedFromMethod )
{
var model = db.Widgets.Find(item.id);
model.PositionColumn = item.PositionColumn;
model.PositionRow = item.PositionRow;
}
I am iterating through the updated data and updating my database table by ID.
So I am interested to know which method is the better way of doing this, and maybe there is an option in Entity Framework to measure the performance of these two tasks? Thanks for your time in answering.
Neither is really efficient.
The first option loops through newDataReturnedFromMethod for each iteration of previousDatafromTable. That's a lot of iterations.
The second options probably executes a database query for each iteration of newDataReturnedFromMethod.
It's far more efficient to join:
var query = from n in newDataReturnedFromMethod
join p in previousDatafromTable on n.id equals p.id
select new { n,p };
foreach (var pair in query)
{
pair.p.PositionColumn = pair.n.PositionColumn;
pair.p.PositionRow = pair.n.PositionRow;
}
EF doesn't have built-in performance measurements. You'd typically use a profiler for that, or the StopWatch class.

LINQ to Entites: Doing a count over one-to-many relationships

I have a couple of tables where there are one to many relationships. Let's say I have a Country table, a State table with a FK to Country, and a City table with a FK to State.
I'd like to be able to create a count of all Cities for a given country, and also a count of cities based on a filter - something like:
foreach( var country in Model.Country ) {
total = country.State.All().City.All().Count() ;
filtered = country.State.All().City.Any(c=>c.field == value).Count();
}
Obviously, this doesn't work - is there any way to do this?
Update:
I can iterate thru the objects:
foreach (var item in Model ) {
... other stuff in here ...
int tot = 0;
int filtered = 0;
foreach (var state in item.State)
{
foreach (var city in state.City)
{
tot++;
if (city.Field == somevalue)
filtered ++;
}
}
... other stuff in here ...
}
but that doesn't seem very elegant.
Update: #AD has a couple of suggestions, but what worked to solve the problem was:
int tot = item.States.Sum(s=>s.City.Count);
int filtered = item.States.Sum(s=>s.City.Where(c=>c.Field == somevalue).Count());
You can try, assuming you already have the givenCountry and value variable populated:
int total = EntityModel.CitySet.Where( it => it.State.Country.ID == givenCountry.ID ).Count();
Above, you take your entire set of cities (EntityMode.CitySet). This set contains all the cities in all the states in all the countries. The problem becomes: what subset of those cities are in country 'givenCountry'? To figure it out, you apply the Where() to the entire set and you compare the countries id to see if they are the same. However, since the city only knows which state it is in (and not the country) you first have to reference its state (it.State). it.State references the state object and that object has a Country property that will reference the country. Then it.State.Country references the country 'it' is in and 'it' is the city, creating a link between the city and the country.
Note that you could have done this is reverse as well with
int total = givenCountry.Sum( c => c.States.Sum( s.Cities.Count() ) )
However, here you will have to make sure that givenCountry has its States collection loaded in memory and also that each State has its Cities collection loaded. That is because you are using Linq-to-Entities on a loaded object and not on an Entity Framework instance object has was the case in the first example. There is a way to craft the last query to use the entity framework object however:
int total = EntityModel.CountrySet.Where( c => c.ID == givenCountry.ID ).Sum( c => c.States.Sum( s.Cities.Count() ) )
As for the number of cities with a specific field, you take a similar approach with a Where() call:
int filtered = EntityModel.CitySet.Where( it => it.field == value ).Count();
Why dont you reverse it?
foreach( var country in Model.Country ) {
var city = Model.State.Where(x=>x.StateID==country.State.StateID).Select(x=>City)
total = city.Count();
filtered = city.All(c=>c.field == value).Count();
}
You have to explicitly load children in the Entity Framework. If you load all the children then you can get counts just fine.
IEnumberable<Country> countries = Model.Country.Include("State");
total = countries[i].State.Count();
Assuming of course that the iteration through all countries is important. Otherwise why not just query against City filtered by State and Country?
In your state foreach you should just be able to do
tot += state.City.Where(x=> x.Field == value).Count();