Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions mdutils/mdutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,19 @@ def new_table(
self.___update_file_data(text_table)

return text_table

def new_table_array(self, data: List[List[str]], text_align: Optional[Union[str, list]] = None, marker: str = ""):
"""
Create a table from a list of lists.
"""
new_table = mdutils.tools.Table.Table()
text_table = new_table.create_table_array(data, text_align)
if marker:
self.file_data_text = self.place_text_using_marker(text_table, marker)
else:
self.___update_file_data(text_table)

return text_table

def new_paragraph(
self,
Expand Down
54 changes: 54 additions & 0 deletions mdutils/tools/Table.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,60 @@ def create_table(
else:
raise ValueError("columns * rows is not equal to text length")

def create_table_array(self, data: List[List[str]], text_align: Optional[Union[str, list]] = None):
"""
Create a table from a list of lists.

This method takes a 2D array (list of lists) and creates a markdown table.
It normalizes the data by converting all elements to strings and ensuring
all rows have the same number of columns by padding with empty strings.

:param data: A 2D list where each inner list represents a row
:type data: List[List[str]]
:param text_align: text align argument, same as create_table.
Values available are: 'right', 'center', 'left' and None (default).
If text_align is a list then individual alignment can be set for each column.
:type text_align: Optional[Union[str, list]]
:return: a markdown table string
:rtype: str

:Example:
>>> from mdutils.tools.Table import Table
>>> data = [['Header 1', 'Header 2'], ['Row 1 Col 1', 'Row 1 Col 2'], ['Row 2 Col 1']]
>>> table = Table().create_table_array(data=data, text_align='center')
>>> print(repr(table))
'\\n|Header 1|Header 2|\\n| :---: | :---: |\\n|Row 1 Col 1|Row 1 Col 2|\\n|Row 2 Col 1||\\n'
"""
if not data:
return ""

# Find the maximum column count across all rows
max_columns = max(len(row) for row in data) if data else 0

if max_columns == 0:
return ""

# Normalize the data: convert all elements to strings and pad rows
normalized_data = []
for row in data:
# Convert all elements to strings
str_row = [str(cell) for cell in row]
# Pad with empty strings if the row is shorter than max_columns
while len(str_row) < max_columns:
str_row.append("")
normalized_data.append(str_row)

# Flatten the 2D array into a 1D list
flattened_text = []
for row in normalized_data:
flattened_text.extend(row)

# Calculate dimensions
rows = len(normalized_data)
columns = max_columns

# Call the existing create_table method
return self.create_table(columns=columns, rows=rows, text=flattened_text, text_align=text_align)

if __name__ == "__main__":
import doctest
Expand Down
193 changes: 193 additions & 0 deletions tests/test_tools/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,196 @@ def test_invalid_text_align(self):
self.assertRaises(
ValueError, table.create_table, 3, 14, text_array, "invalid_align"
)

def test_create_table_array_basic(self):
"""Test basic functionality of create_table_array method."""
table = Table()
data = [
["Header 1", "Header 2", "Header 3"],
["Row 1 Col 1", "Row 1 Col 2", "Row 1 Col 3"],
["Row 2 Col 1", "Row 2 Col 2", "Row 2 Col 3"]
]
expected_result = (
"\n|Header 1|Header 2|Header 3|\n| --- | --- | --- |\n"
"|Row 1 Col 1|Row 1 Col 2|Row 1 Col 3|\n"
"|Row 2 Col 1|Row 2 Col 2|Row 2 Col 3|\n"
)

result = table.create_table_array(data=data)
self.assertEqual(result, expected_result)

def test_create_table_array_with_center_alignment(self):
"""Test create_table_array with center text alignment."""
table = Table()
data = [
["Header 1", "Header 2"],
["Data 1", "Data 2"]
]
expected_result = (
"\n|Header 1|Header 2|\n| :---: | :---: |\n"
"|Data 1|Data 2|\n"
)

result = table.create_table_array(data=data, text_align="center")
self.assertEqual(result, expected_result)

def test_create_table_array_with_left_alignment(self):
"""Test create_table_array with left text alignment."""
table = Table()
data = [
["Header 1", "Header 2"],
["Data 1", "Data 2"]
]
expected_result = (
"\n|Header 1|Header 2|\n| :--- | :--- |\n"
"|Data 1|Data 2|\n"
)

result = table.create_table_array(data=data, text_align="left")
self.assertEqual(result, expected_result)

def test_create_table_array_with_right_alignment(self):
"""Test create_table_array with right text alignment."""
table = Table()
data = [
["Header 1", "Header 2"],
["Data 1", "Data 2"]
]
expected_result = (
"\n|Header 1|Header 2|\n| ---: | ---: |\n"
"|Data 1|Data 2|\n"
)

result = table.create_table_array(data=data, text_align="right")
self.assertEqual(result, expected_result)

def test_create_table_array_with_mixed_alignment(self):
"""Test create_table_array with mixed text alignment per column."""
table = Table()
data = [
["Left", "Center", "Right"],
["L1", "C1", "R1"],
["L2", "C2", "R2"]
]
expected_result = (
"\n|Left|Center|Right|\n| :--- | :---: | ---: |\n"
"|L1|C1|R1|\n|L2|C2|R2|\n"
)

result = table.create_table_array(data=data, text_align=["left", "center", "right"])
self.assertEqual(result, expected_result)

def test_create_table_array_uneven_rows(self):
"""Test create_table_array with rows of different lengths."""
table = Table()
data = [
["Header 1", "Header 2", "Header 3"],
["Row 1 Col 1", "Row 1 Col 2"], # Missing third column
["Row 2 Col 1"] # Missing second and third columns
]
expected_result = (
"\n|Header 1|Header 2|Header 3|\n| --- | --- | --- |\n"
"|Row 1 Col 1|Row 1 Col 2||\n"
"|Row 2 Col 1|||\n"
)

result = table.create_table_array(data=data)
self.assertEqual(result, expected_result)

def test_create_table_array_mixed_data_types(self):
"""Test create_table_array with mixed data types (converts to strings)."""
table = Table()
data = [
["Name", "Age", "Score", "Active"],
["Alice", 25, 95.5, True],
["Bob", 30, 87.2, False],
["Charlie", None, 92.0, True]
]
expected_result = (
"\n|Name|Age|Score|Active|\n| --- | --- | --- | --- |\n"
"|Alice|25|95.5|True|\n"
"|Bob|30|87.2|False|\n"
"|Charlie|None|92.0|True|\n"
)

result = table.create_table_array(data=data)
self.assertEqual(result, expected_result)

def test_create_table_array_empty_data(self):
"""Test create_table_array with empty data."""
table = Table()
data = []
expected_result = ""

result = table.create_table_array(data=data)
self.assertEqual(result, expected_result)

def test_create_table_array_single_row(self):
"""Test create_table_array with a single row."""
table = Table()
data = [["Single", "Row", "Data"]]
expected_result = (
"\n|Single|Row|Data|\n| --- | --- | --- |\n"
)

result = table.create_table_array(data=data)
self.assertEqual(result, expected_result)

def test_create_table_array_single_column(self):
"""Test create_table_array with a single column."""
table = Table()
data = [["Header"], ["Row 1"], ["Row 2"]]
expected_result = (
"\n|Header|\n| --- |\n"
"|Row 1|\n|Row 2|\n"
)

result = table.create_table_array(data=data)
self.assertEqual(result, expected_result)

def test_create_table_array_with_special_characters(self):
"""Test create_table_array with special characters that need escaping."""
table = Table()
data = [
["Header|With|Pipes", "Normal Header"],
["Data|With|Pipes", "Normal Data"]
]
expected_result = (
"\n|Header\\|With\\|Pipes|Normal Header|\n| --- | --- |\n"
"|Data\\|With\\|Pipes|Normal Data|\n"
)

result = table.create_table_array(data=data)
self.assertEqual(result, expected_result)

def test_create_table_array_rows_with_empty_lists(self):
"""Test create_table_array with some empty rows."""
table = Table()
data = [
["Header 1", "Header 2"],
[], # Empty row
["Row 2 Col 1", "Row 2 Col 2"]
]
expected_result = (
"\n|Header 1|Header 2|\n| --- | --- |\n"
"|||\n"
"|Row 2 Col 1|Row 2 Col 2|\n"
)

result = table.create_table_array(data=data)
self.assertEqual(result, expected_result)

def test_create_table_array_with_partial_alignment_list(self):
"""Test create_table_array with alignment list shorter than number of columns."""
table = Table()
data = [
["Col 1", "Col 2", "Col 3"],
["Data 1", "Data 2", "Data 3"]
]
expected_result = (
"\n|Col 1|Col 2|Col 3|\n| :--- | :---: | --- |\n"
"|Data 1|Data 2|Data 3|\n"
)

result = table.create_table_array(data=data, text_align=["left", "center"])
self.assertEqual(result, expected_result)