How can I iterate over list items in Pandoc's lua-filter function? - lua

Pandoc's lua filter makes it really easy to iterate over a document and munge the document as you go. My problem is I can't figure out how to isolate list item elements. I can find lists and the block level things inside each list item, but I can't figure out a way to iterate over list items.
For example let's say I had the following Markdown document:
1. One string
Two string
2. Three string
Four string
Lets say I want to make the first line of each list item bold. I can easily change how the paragraphs are handled inside OrderedLists, say using this filter and pandoc --lua-filter=myfilter.lua --to=markdown input.md
local i
OrderedList = function (element)
i = 0
return pandoc.walk_block(element, {
Para = function (element)
i = i + 1
if i == 1 then return pandoc.Para { pandoc.Strong(element.c) }
else return element end
end
})
end
This will indeed change the first paragraph element to bold, but it only changes the first paragraph of the first list item because it's iterating across all paragraphs in all list items in the list, not on each list item, then on each paragraph.
1. **One string**
Two string
2. Three string
Four string
If I separate the two list items into two separate lists again the first paragraph of the first item is caught, but I want to catch the first paragraph of every list item! I can't find anything in the documentation about iterating over list items. How is one supposed to do that?

The pandoc Lua filter docs have recently been updated with more info on the properties of each type. E.g., for OrderedList elements, the docs should say (it currently says items instead of content, which is a bug):
OrderedList
An ordered list.
content: list items (List of Blocks)
listAttributes: list parameters (ListAttributes)
start: alias for listAttributes.start (integer)
style: alias for listAttributes.style (string)
delimiter: alias for listAttributes.delimiter (string)
tag, t: the literal OrderedList (string)
So the easiest way is to iterate over the content field and change items therein:
OrderedList = function (element)
for i, item in ipairs(element.content) do
local first = item[1]
if first and first.t == 'Para' then
element.content[i][1] = pandoc.Para{pandoc.Strong(first.content)}
end
end
return element
end

Related

Dart, compare two lists and return element from first list that does not exist in second list

I have two lists,
List first = [{'name':'ABC','serialNumber':'ABC-124-353'},
{'name':'XYZ','serialNumber':'XYZ-123-567'},
{'name':'GRE', 'serialNumber': 'GRE-290-128'}];
List second = [{'name':'PQR','serialNumber':'PQR-D123-SII23'},{'name':'GAR','serialNumber':'GAR-G43-432'},
{'name':'MNOP','serialNumber':'XYZ-123-567'}];
Is there any easier way to compare first list and second list by serialNumber.
such that element from first list that doesn't exist in second list are outputted as a result.
So in this case
[{'name':'ABC','serialNumber':'ABC-124-353'},{'name':'GRE', 'serialNumber': 'GRE-290-128'}]
from first list is desired output, because ABC-124-353 and GRE-290-128 doesn't exist in list second
Another solution would be to use where on your first List to check if the serialNumber is contained in the second list:
final secondSerials = second.map((item) => item['serialNumber']).toSet();
print(first.where((item) => !secondSerials.contains(item['serialNumber'])).toList());
I'd make a set of the serial numbers of the second list, so that you can do efficient contains checks.
So:
var secondListSerials = {for (var entry in secondList) entry["serialNumber"]};
var firstListOnly = [for (var entry in firstList)
if (!secondListSerials.contains(entry["serialNumber"]) entry
];

How to add same value to a list multiple times in one go in Dart/Flutter

Let's say I have an empty list.
If I want to add a certain number of letters "g" to this list. for example 30 40 etc. the number I send may change.
Is there a way to do this in one go without using a loop?
What is it, if any?
https://api.flutter.dev/flutter/dart-core/List/fillRange.html
I need a method like fillRange.
FillRange does not work on an empty list.
If the list is empty, don't bother using it. Just generate a new list with List.filled:
final list = List.filled(30, 'g');
EDIT: For completeness, here is how to use the above with an existing list:
final list = <String>[...];
// Mutate original list
list.addAll(List.filled(30, 'g'));
// Create new list with spread syntax
final newList = [
...list,
...List.filled(30, 'g'),
];

Creating lists in google sheets

Wasn't really sure how else to word the title, but here's a little more information!
In this spreadsheet: https://docs.google.com/spreadsheets/d/1oXLbc9vkjuWYU60xhsYsT3vv7utOJswVjwJUWS3XpF0/edit?usp=sharing
In the 'Lists by Item Type' sheet, I would like to make lists of all the items separated by item types for use in data validation elsewhere. Is there a function similar to =UNIQUE() that will give me a list of all items with the value x in column Q where x is the item type I input?
Use:
=FILTER(
/* all item names ('Item Database'!$A$3:$A) */,
/* all item types ('Item Database'!$Q$3:$Q) */ = /* the needed item type */
)
This will give you all the items of a given type.

Problems with lua filter to iterate over table rows

I'm trying to write a simply lua filter for pandoc in order to do some macro expansion for the elements in a ReST Table.
filter.lua
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
function Table(table)
elems=pandoc.Table(table)["rows"]
print(tablelength(table))
for v in pairs(elems) do
print(v) -- Prints nothings
end
return table
end
test.rst
======= =========
A B
======= =========
{{x}} {{y}}
======= =========
Now, if I run pandoc.exe -s --lua-filter filter.lua test.rst -t rst the program says that there are 5 elements in elems, but the for loop is just skipped and I really don't know what I'm doing wrong here.
I'm very new to Lua and also know pandoc very litte. How can I iterate over the elements in elems?
Pandoc lua-filters provide the handy walk_block helper, that recursively walks the document tree down and applies a function to the elements that match the key.
In the example below, we give walk_block a lua table (what's a map or dict in other languages) with only one key (the key Str), and the value of the table is the function to apply. The function checks for the braces, strips them and prepends foo.
function Table(table)
return pandoc.walk_block(table, {
Str = function(el)
if el.text:sub(1,2) == '{{' then
txt = 'foo' .. el.text:sub(3, -3)
else
txt = el.text
end
return pandoc.Str(txt)
end
})
end
There are a couple of areas of misunderstanding in your code. First you need to remember that everything in lua is a table (implemented as an associative array or dictionary), and arrays are just a special case of a table where the keys are integers. To avoid confusion, for the rest of this answer I will use Table when I'm referring to the pandoc document element, and table when I'm referring to the lua data structure.
Your tablelength function is just counting the number of elements in the pandoc table that represents a Table. If you look in https://www.pandoc.org/lua-filters.html#type-ref-Block you will see that a Table has 5 properties- caption, aligns, widths, headers and rows. The return value of this function is 5 because of this. If you print out the values in the loop inside tablelength then you will confirm this. If you want to count the rows then you will need to pass the rows array into the function, not the table.
The second problem is that you are creating a new table rather than using the one passed in by pandoc. Instead of using elems=pandoc.Table(table)["rows"] just use elems=table["rows"] or elems=table.rows which is equivalent. The function pandoc.Table() is used to create a new element.
Furthermore, to loop over the elements in a table that are in the form of an array, you can use the ipairs function- it will return the numerically indexed values as described here What is the difference of pairs() vs. ipairs() in Lua?.
The rows table is, as might be expected, an array of rows where each row is in turn an array of elements. So to access the elements of the table you will need to have two loops.
Finally there is the issue of the pandoc object model. Because a table can contain other things (images, links, bold text etc) the final cell value is actually a list of blocks. Now depending on what you want to do with the table you can deal with this in different ways. You can use the walk_block function that mb21 referred to, but looping over just the blocks in a single cell. If your Table only contains (unformatted) text then you can simplify things by using the stringify function, which collapses the list of blocks into a single string.
Putting all of this together gives the following modified version of your code.
local stringify=pandoc.utils.stringify
-- This function is no longer needed
function tablelength(T)
local count = 0
for e in pairs(T) do
count = count + 1
print(e) -- this shows the key not the value
end
return count
end
function Table(table)
rows=table["rows"]
print("TableLength="..#rows)
for rownum,row in ipairs(rows) do
for colnum, elem in ipairs(row) do
print(stringify(elem)) -- Prints cell text
end
end
return table
end
As to you followup question, if you want to modify things then you just need to replace the cell values, while respecting pandoc's object model. You can construct the various types used by pandoc with the constructors in module pandoc (such as the aforementioned pandoc.Table). The simplest table cell will be an array with a single Plain block, which in turn contains a single Str element (blocks usually contain a list of Inline elements).
The following code shows how you can modify a table using the existing content or the row/column number. Note that I changed the parameter to the Table function from table to tableElem because table is a common type used in lua and overriding it gives hard to track errors.
local stringify=pandoc.utils.stringify
function makeCell(s)
return {pandoc.Plain({pandoc.Str(s)})}
end
function Table(tableElem)
rows=tableElem["rows"]
for rownum,row in ipairs(rows) do
for colnum, elem in ipairs(row) do
local elemText=stringify(elem)
if elemText=="{{x}}" then
row[colnum]=makeCell(elemText:gsub("x","newVal"))
end
if rownum==1 and colnum==2 then
row[colnum]=makeCell("Single cell")
end
end
end
local newRow={ makeCell("New A"), makeCell("New B")}
table.insert(rows,newRow)
return tableElem
end

Append new ListItem to particular list in Google Docs API

I'm looking for a way to append new ListItem to particular list in Google Docs API.
My problem is that I can't figure out how to determine last item in list, so I could just insert new item using it's index.
My vision of how to implement this looks like:
body.insertListItem(body.getChildIndex(lastItem) + 1, newItem);
P.S. Am I doing something wrong, maybe there is some other way to implement appending?
Thanks in advance!
A ListItem is a Paragraph that is associated with a list ID. A ListItem may contain Equation, Footnote, HorizontalRule, InlineDrawing, InlineImage, PageBreak, and Text elements. For more information on document structure, see the guide to extending Google Docs.
ListItems with the same list ID belong to the same list and are numbered accordingly. The ListItems for a given list are not required to be adjacent in the document or even have the same parent element. Two items belonging to the same list may exist anywhere in the document while maintaining consecutive numbering, as the following example illustrates:
var body = DocumentApp.getActiveDocument().getBody();
// Append a new list item to the body.
var item1 = body.appendListItem('Item 1');
// Log the new list item's list ID.
Logger.log(item1.getListId());
// Append a table after the list item.
body.appendTable([
['Cell 1', 'Cell 2']
]);
// Append a second list item with the same list ID. The two items are treated as the same list,
// despite not being consecutive.
var item2 = body.appendListItem('Item 2');
item2.setListId(item1);

Resources