diff --git a/curlytag.js b/curlytag.js index 5ecc28d..91c43a3 100644 --- a/curlytag.js +++ b/curlytag.js @@ -326,6 +326,27 @@ class CurlyTag { join: (value, seperator = ' ') => { return value.join(seperator); }, + array_to_sentence_string: (value, connector = 'and') => { + const items = Array.isArray(value) + ? value.map((item) => String(item ?? '')).filter((item) => item !== '') + : []; + + const word = String(connector ?? 'and'); + + if (items.length === 0) { + return ''; + } + + if (items.length === 1) { + return items[0]; + } + + if (items.length === 2) { + return `${items[0]} ${word} ${items[1]}`; + } + + return `${items.slice(0, -1).join(', ')}, ${word} ${items[items.length - 1]}`; + }, reverse: (value) => { if (Array.isArray(value)) { return [...value].reverse(); diff --git a/tests/filters/array/array-to-sentence-string.test.js b/tests/filters/array/array-to-sentence-string.test.js new file mode 100644 index 0000000..c644d55 --- /dev/null +++ b/tests/filters/array/array-to-sentence-string.test.js @@ -0,0 +1,48 @@ +import { describe, expect, test } from 'vite-plus/test'; +import { template } from '#curlytag'; + +describe('array_to_sentence_string', () => { + test('joins three items with and', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: ['one', 'two', 'three'] })).toBe('one, two, and three'); + }); + + test('joins two items with and', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: ['one', 'two'] })).toBe('one and two'); + }); + + test('returns single item as string', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: ['one'] })).toBe('one'); + }); + + test('returns empty string for empty array', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: [] })).toBe(''); + }); + + test('uses custom connector', () => { + expect(template.parse("{{ value | array_to_sentence_string: 'or' }}", { value: ['one', 'two', 'three'] })).toBe('one, two, or three'); + }); + + test('uses custom connector for two items', () => { + expect(template.parse("{{ value | array_to_sentence_string: 'or' }}", { value: ['one', 'two'] })).toBe('one or two'); + }); + + test('handles four items', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: ['a', 'b', 'c', 'd'] })).toBe('a, b, c, and d'); + }); + + test('handles numeric values', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: [1, 2, 3] })).toBe('1, 2, and 3'); + }); + + test('skips null items', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: ['one', null, 'three'] })).toBe('one and three'); + }); + + test('skips undefined items', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: ['one', undefined, 'three'] })).toBe('one and three'); + }); + + test('returns empty string for array of nulls', () => { + expect(template.parse('{{ value | array_to_sentence_string }}', { value: [null, null] })).toBe(''); + }); +});