diff --git a/mdutils/mdutils.py b/mdutils/mdutils.py index 669dacb..991d4af 100644 --- a/mdutils/mdutils.py +++ b/mdutils/mdutils.py @@ -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, diff --git a/mdutils/tools/Table.py b/mdutils/tools/Table.py index a5c24b9..53069a0 100644 --- a/mdutils/tools/Table.py +++ b/mdutils/tools/Table.py @@ -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 diff --git a/tests/test_tools/test_table.py b/tests/test_tools/test_table.py index a2c2e39..9571459 100644 --- a/tests/test_tools/test_table.py +++ b/tests/test_tools/test_table.py @@ -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)