Why is 'RecursiveExpand' not recognised in this Power Query - powerquery

I've got this Power Query code, that I feel should be working, however it keeps popping up with "Expression.Error: The name 'RecursiveExpand' wasn't recognized. Make sure it's spelled correctly."
Code is calling an API to get data from JIRA, then to expand all record type columns dynamically.
let
Source = 100,
JiraIDPerPage = 100,
GetJson = (maxR, startA) =>
let
Json = Json.Document(
Web.Contents(
#"URI_BASE (2)",
[
RelativePath = #"URI_PATH (2)" & #"URI_RESOURCE (2)",
Content = Json.FromValue(
[
jql = "project in (" & #"JIRA_PROJECT_KEYS (2)" & ")",
fields = {"*all"},
maxResults = maxR,
startAt = startA
]
),
Headers =
[
Authorization = "Basic " & Binary.ToText(Text.ToBinary(#"USERNAME_TOKEN (2)"), 0),
Accept = "application/json",
#"Content-Type"="application/json"
]
]
)
)
in Json,
GetJiraIDCount = () =>
let maxResultss = 0,
startAtt = 0,
Json = GetJson(maxResultss, startAtt),
Count = Json[#"total"]
in Count,
GetPage = (Index) =>
let Top = 100,
Skip = Index * 100,
Json = GetJson(Top, Skip),
Value = Json[#"issues"]
in Value,
JiraIDCount = List.Max({ JiraIDPerPage, GetJiraIDCount() }),
PageCount = Number.RoundUp(JiraIDCount / JiraIDPerPage),
PageIndices = { 0 .. PageCount - 1 },
Pages = List.Transform(PageIndices, each GetPage(_)),
JiraID = List.Union(Pages),
Table = Table.FromList(JiraID, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Expanded Column1" = Table.ExpandRecordColumn(Table, "Column1", {"expand", "id", "self", "key", "fields"}, {"expand", "id", "self", "key", "fields"}),
#"Removed Columns" = Table.RemoveColumns(#"Expanded Column1",{"expand", "id", "self"}),
Custom1 = let
// Function to recursively expand a table
RecursiveExpand = (table as table) =>
let
// Get a list of columns that are lists
listColumns = Table.ColumnsOfType(table, {type list}),
// Use List.Accumulate to expand each list column
expandedTable = List.Accumulate(listColumns, table, (state, current) => Table.ExpandListColumn(state, current[Name])),
// Check if there are still nested lists
nestedListsExist = Table.ColumnsOfType(expandedTable, {type list}),
// If nested lists still exist, call the function recursively
result = if List.IsEmpty(nestedListsExist) then expandedTable else RecursiveExpand(expandedTable)
in
result,
// Start the recursive expansion
expandedTable = RecursiveExpand(#"Removed Columns")
in
expandedTable
in
Custom1
I've searched for an answer to find why it's not recognised but I'm not finding any answers.

You need an # to refer to your recursive call in code.
result = if List.IsEmpty(nestedListsExist) then expandedTable else #RecursiveExpand(expandedTable)

Related

How to make a PUT/PATCH request with ListField field in Django-REST?

I am trying to build a rest-api for a movie-review website. The movie model contains a cast-field which is a list-field, when using ModelViewSets one can't POST ListFields through HTML, so I set blank = true for all list-fields thinking that I'll make a raw PATCH request to update the blank fields, but I am unable to do so.
models.py
class Movie(models.Model):
movie_name = models.CharField(max_length = 100, unique = True)
release_date = models.DateField(blank = True)
description = models.TextField(max_length = 500)
movie_poster = models.ImageField(blank = True)
directors = ListCharField(
base_field = models.CharField(max_length = 500),
max_length = 6 * 500,
blank = True
)
trailer_url = models.URLField()
cast = ListCharField(
base_field = models.CharField(max_length = 225),
max_length = 11 * 225,
blank = True
)
genre = ListCharField(
base_field = models.CharField(max_length = 225),
max_length = 11 * 255,
blank = True
)
avg_rating = models.FloatField(validators = [MinValueValidator(0), MaxValueValidator(5)])
country = models.CharField(max_length = 100)
language = models.CharField(max_length = 100)
budget = models.BigIntegerField(blank = True)
revenue = models.BigIntegerField(blank = True)
runtime = models.DurationField(blank = True)
Serializer
class MovieSerializer(ModelSerializer):
cast = ListField(
child = CharField(required = False), required = False,
min_length = 0
)
genre = ListField(
child = CharField(required = False), required = False,
min_length = 0
)
directors = ListField(
child = CharField(required = False), required = False,
min_length = 0
)
class Meta:
model = Movie
fields = '__all__'
I used djano-mysql for adding the ListCharField field-type.
https://i.stack.imgur.com/sC6Vw.png [The data without list field values]
https://i.stack.imgur.com/W3xea.png [request I tried to make]
https://i.stack.imgur.com/OPeJn.png [response that I received]
Original put request which resulted in an error response
https://i.stack.imgur.com/W3xea.png
The request had some trailing commas, due to which the API expected more values.
Here's the correct request-content -
{
"cast": [
"aamir",
"sakshi"
],
"genre": [
"biopic"
],
"directors": [
"nitesh tiwari"
]
}

how to call multiple function in action?

I am facing a problem while calling multiple functions through AJAX. What i mainly have to do is to call the different functions through AJAX.
This should be working such that when i try to call those functions, they should be sent a request and the response should be the returned values of those function, but i am getting the error while doing so. Can anyone suggest me how to make it work?
public virtual IQueryable<CardViewModel> ChannelQuery(PageInfo p)
{
var q = from client in my.Clients(0, 0)
join m in db.Channels on client.ClientId equals m.ClientId
join c in db.Contents on m.ChannelId equals c.ContentId
where c.ContentName.Contains(p.Where) || m.ChannelShortDescription.Contains(p.Where) || m.ChannelName.Contains(p.Where)
select new CardViewModel
{
Name = "Channel",
ID = m.ChannelId,
Title = m.ChannelName,
Description = m.ChannelShortDescription,
Pic = new VZDev.ViewModels.Pic { width = 255, height = 170, source = `enter code here`m.ChannelLogo, text = m.ChannelName },
PictureViewTemplate = "_PicBanner",
ShowTools = true
};
return q;
}
public virtual IQueryable<CardViewModel> ContentsQueries(PageInfo p)
{
var query = from content in db.Contents
join clients in my.Clients(0, 0) on content.ClientId equals clients.ClientId
join m in db.Channels on clients.ClientId equals m.ClientId
where content.ContentName.Contains(p.Where) || `enter code here`m.ChannelShortDescription.Contains(p.Where) || m.ChannelName.Contains(p.Where)
select new CardViewModel
{
Name = "Content",
ID = content.ContentId,
Title = content.ContentName,
Description = clients.ResellerName,
Pic = new VZDev.ViewModels.Pic { width = 255, height = 170, source = `enter code here`"None", text = content.ContentName },
PictureViewTemplate = "_PicBanner",
ShowTools = true
};
return query;
}
public ViewResult Advance(PageInfo p)
{
return View(ChannelQuery(p).ToPagedResult(p, "ID Desc"));
}
You can only call one function per AJAX call. If you need Channel and Content data in one request, make a dictionary to hold both sets of values. Something like this:
Dictionary<string,object> data = new Dictionary<string,object>();
data.Add("Channel", ChannelQuery());
data.Add("Content", ContentsQuery());

syntax to return a number of results per page

I have this linq statement that returns result and i wanted to add the specifications of how many items are displayed per page. I know the default is 10 per page how can i change it to 40?
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
});
Get User
public virtual IEnumerable<T> GetUser<T>(int userId, Func<IDataRecord, T> modelBinder, int resultsPerPage = 10, int pageNumber = 1)
{
if (userId < 1)
throw new NullReferenceException("The sectionId cannot be null, when retreiving an element");
if (resultsPerPage < 1)
resultsPerPage = 1; // enforce bare minimum result set
if (pageNumber < 1)
pageNumber = 1; // enforce one-based page numbering
SqlCommand _command = new SqlCommand("dbo.GetUser");
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.Add(new SqlParameter { ParameterName = "userId", SqlDbType = SqlDbType.Int, Value = userId });
_command.Parameters.Add(new SqlParameter { ParameterName = "resultsPerPage", SqlDbType = SqlDbType.Int, Value = resultsPerPage });
_command.Parameters.Add(new SqlParameter { ParameterName = "pageNumber", SqlDbType = SqlDbType.Int, Value = pageNumber });
return DbInstance.ExecuteAs<T>(_command, modelBinder);
}
Neither Linq nor entity framework have any default number of records 'per page'. But since your GetUser function includes a resultsPerPage parameter, you can just do this:
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
}, 40);
To limit the number of results in Linq use the the Enumerable.Take method:
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
}).Take(40);

Referencing the collection your working on within LINQ

I am working with linq, and im wondering if there is any way that I might reference the collection I am working on from within my linq code? What I am looking for is something like this:
let result = (from t in someCollection
where t == something
select t).Where(res => res.start == THIS.Min(temp => temp.start))
So what I want to achieve in this query is that the THIS variable should provide a reference to the collection that the where clause is being applied to:
(from t in someCollection
where t == something
select t)
There are lots of ways to get around this problem, but I am specifically interested in a way of using a reference to the collection in use. Hope some of you know something about this!
Thanks!
The way to do what your example states is like this:
var minValue = someCollection.Min(x => x.start);
var result = from t in someCollection
where t.id > 5 // replace this line with your "where t == something"
where t.start == minValue
select t;
but say that you have to do some kind of other comparison for every
element in your collection to every other element. Is some there some
way of doing a thing like this?
If you really need to compare one item with every other item in the list, you could pattern your code like this:
var result = from t in someCollection
where t.id > 5 // replace this line with your "where t == something"
let minValue = someCollection.Min (x => x.start)
where t.start == minValue
select t;
The problem with the second example is that every item you visit in your someCollection it will be forced to recalculate the minValue.
Now, here's a completely contrived example that illustrates having to access the entire collection while accessing each member of the collection. It simply goes through a list of items and outputs each item along with all the other items that have lesser dates:
var eventItems = new[]
{
new { Name = "alpha", DateCreated = DateTime.Today.AddDays(1) },
new { Name = "bravo", DateCreated = DateTime.Today.AddDays(2) },
new { Name = "charlie", DateCreated = DateTime.Today.AddDays(-1) },
new { Name = "delta", DateCreated = DateTime.Today.AddDays(-5) },
new { Name = "echo", DateCreated = DateTime.Today.AddDays(-3) },
new { Name = "foxtrot", DateCreated = DateTime.Today.AddDays(3) },
new { Name = "golf", DateCreated = DateTime.Today.AddDays(-4) }
};
var results = from item in eventItems
where item.Name.Length > 2
let prevDays = eventItems.Where (i => i.DateCreated < item.DateCreated)
select new
{
Name = item.Name,
CurrentDate = item.DateCreated,
PreviousItems = prevDays
};
The output:
Perhaps one of these examples will help you with your exact problem.

DefaultIfEmpty for LINQ to a DataTable?

I have a LINQ query written to pull at least one row from a datatable as follows:
var generalQuery =
from contact in contacts.AsEnumerable().DefaultIfEmpty()
where contact.Field<String>("CONTACT_TYPE").ToUpper() == "AGENT"
select new
{
AgentName = contact.Field<String>("FIRST_NAME") + " " +
contact.Field<String>("LAST_NAME"),
AgentPhoneNumber = contact.Field<String>("PHONE"),
AgentEmailAddress = contact.Field<String>("EMAIL")
};
I then try to set the properties of a new instance of a class with the values in the LINQ query output:
var genInfo = new GeneralInformationType
{
AgentName = generalQuery.FirstOrDefault().AgentName,
AgentPhoneNumber = generalQuery.FirstOrDefault().AgentPhoneNumber,
AgentEmailAddress = generalQuery.FirstOrDefault().AgentEmailAddress
};
The problem I am running into is that occasionally there are no results in the query. This tries to set the value of the various strings in GeneralInformationType to null and causes exceptions. I tried various methods of DefaultIfEmpty, but can't figure out where to put it.
Any help would be appreciated.
Thanks,
Rob
I would recommend removing DefaultIfEmpty() in your generalQuery and adding some logic when generalQuery is empty like so:
var generalQuery =
from contact in contacts.AsEnumerable()
where contact.Field<String>("CONTACT_TYPE").ToUpper() == "AGENT"
select new
{
AgentName = contact.Field<String>("FIRST_NAME") + " " +
contact.Field<String>("LAST_NAME"),
AgentPhoneNumber = contact.Field<String>("PHONE"),
AgentEmailAddress = contact.Field<String>("EMAIL")
};
var genInfo = new GeneralInformationType
{
AgentName = generalQuery.Any() ? generalQuery.First().AgentName : "Default Name",
....
};
There are two separate problems here. First, DefaultIfEmpty is going to effectively give you a sequence with a null DataRow if the query doesn't return anything. That will then fail not when it tries to assign strings - but when in the Where clause. You're then also using FirstOrDefault which would normally return null if there are no matching entries.
I would personally remove DefaultIfEmpty call, and put all the defaulting in the code for genInfo:
var generalQuery =
from contact in contacts.AsEnumerable()
where contact.Field<String>("CONTACT_TYPE").ToUpper() == "AGENT"
select new
{
AgentName = contact.Field<String>("FIRST_NAME") + " " +
contact.Field<String>("LAST_NAME"),
AgentPhoneNumber = contact.Field<String>("PHONE"),
AgentEmailAddress = contact.Field<String>("EMAIL")
};
// Like using DefaultIfEmpty(...).First()
var result = generalQuery.FirstOrDefault() ??
new { AgentName = "Default",
AgentPhoneNumber = "Default",
AgentEmailAddress = "Default" };
var genInfo = new GeneralInformationType
{
AgentName = result.AgentName,
AgentPhoneNumber = result.AgentPhoneNumber,
AgentEmailAddress = result.AgentEmailAddress
};
Obviously change "Default" to whatever you want. If you have more specific requirements, please explain what you're trying to do and I'm sure we'll be able to accommodate them...

Resources