Skip to content

Commit

Permalink
feat: pop/shift/unshift filters from Jekyll
Browse files Browse the repository at this point in the history
  • Loading branch information
harttle committed Apr 14, 2024
1 parent 18d5592 commit 258780e
Show file tree
Hide file tree
Showing 17 changed files with 240 additions and 35 deletions.
3 changes: 3 additions & 0 deletions docs/source/_data/sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ filters:
modulo: modulo.html
newline_to_br: newline_to_br.html
plus: plus.html
pop: pop.html
push: push.html
prepend: prepend.html
raw: raw.html
Expand All @@ -63,6 +64,7 @@ filters:
reverse: reverse.html
round: round.html
rstrip: rstrip.html
shift: shift.html
size: size.html
slice: slice.html
sort: sort.html
Expand All @@ -76,6 +78,7 @@ filters:
truncate: truncate.html
truncatewords: truncatewords.html
uniq: uniq.html
unshift: unshift.html
upcase: upcase.html
url_decode: url_decode.html
url_encode: url_encode.html
Expand Down
2 changes: 1 addition & 1 deletion docs/source/filters/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Categories | Filters
Math | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least, at_most
String | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last,remove, remove_first, remove_last, truncate, truncatewords
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br
Array | slice, map, sort, sort_natural, uniq, where, first, last, join, reverse, concat, compact, size, push
Array | slice, map, sort, sort_natural, uniq, where, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
Date | date
Misc | default, json, raw

Expand Down
24 changes: 24 additions & 0 deletions docs/source/filters/pop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: pop
---

{% since %}v10.11.0{% endsince %}

Pop an element from the array. It's NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.

Input
```liquid
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
{% assign everything = fruits | pop %}
{% for item in everything %}
- {{ item }}
{% endfor %}
```

Output
```text
- apples
- oranges
```
24 changes: 24 additions & 0 deletions docs/source/filters/shift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: shift
---

{% since %}v10.11.0{% endsince %}

Shift an element from the array. It's NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.

Input
```liquid
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
{% assign everything = fruits | shift %}
{% for item in everything %}
- {{ item }}
{% endfor %}
```

Output
```text
- oranges
- peaches
```
25 changes: 25 additions & 0 deletions docs/source/filters/unshift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: unshift
---

{% since %}v10.11.0{% endsince %}

Unshift an element to the front of the array. It's NON-DESTRUCTIVE, i.e. it does not mutate the array, but rather make a copy and mutate that.

Input
```liquid
{% assign fruits = "oranges, peaches" | split: ", " %}
{% assign everything = fruits | unshift: "apples" %}
{% for item in everything %}
- {{ item }}
{% endfor %}
```

Output
```text
- apples
- oranges
- peaches
```
2 changes: 1 addition & 1 deletion docs/source/zh-cn/filters/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ LiquidJS 共支持 40+ 个过滤器,可以分为如下几类:
数学 | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least, at_most
字符串 | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last, remove, remove_first, remove_last, truncate, truncatewords
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br
数组 | slice, map, sort, sort_natural, uniq, wheres, first, last, join, reverse, concat, compact, size, push
数组 | slice, map, sort, sort_natural, uniq, wheres, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
日期 | date
其他 | default, json

Expand Down
24 changes: 24 additions & 0 deletions docs/source/zh-cn/filters/pop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: pop
---

{% since %}v10.11.0{% endsince %}

从数组末尾弹出一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。

输入
```liquid
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
{% assign everything = fruits | pop %}
{% for item in everything %}
- {{ item }}
{% endfor %}
```

输出
```text
- apples
- oranges
```
24 changes: 24 additions & 0 deletions docs/source/zh-cn/filters/shift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: shift
---

{% since %}v10.11.0{% endsince %}

从数组头部弹出一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。

输入
```liquid
{% assign fruits = "apples, oranges, peaches" | split: ", " %}
{% assign everything = fruits | shift %}
{% for item in everything %}
- {{ item }}
{% endfor %}
```

输出
```text
- oranges
- peaches
```
25 changes: 25 additions & 0 deletions docs/source/zh-cn/filters/unshift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: unshift
---

{% since %}v10.11.0{% endsince %}

往数组头部添加一个元素。注意该操作不会改变原数组,而是在一份拷贝上操作。

输入
```liquid
{% assign fruits = "oranges, peaches" | split: ", " %}
{% assign everything = fruits | unshift: "apples" %}
{% for item in everything %}
- {{ item }}
{% endfor %}
```

输出
```text
- apples
- oranges
- peaches
```
32 changes: 23 additions & 9 deletions src/filters/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const reverse = argumentsToValue((v: any[]) => [...toArray(v)].reverse())

export function * sort<T> (this: FilterImpl, arr: T[], property?: string): IterableIterator<unknown> {
const values: [T, string | number][] = []
for (const item of toArray(toValue(arr))) {
for (const item of toArray(arr)) {
values.push([
item,
property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
Expand All @@ -25,7 +25,6 @@ export function * sort<T> (this: FilterImpl, arr: T[], property?: string): Itera
}

export function sort_natural<T> (input: T[], property?: string) {
input = toValue(input)
const propertyString = stringify(property)
const compare = property === undefined
? caseInsensitiveCompare
Expand All @@ -37,36 +36,51 @@ export const size = (v: string | any[]) => (v && v.length) || 0

export function * map (this: FilterImpl, arr: Scope[], property: string): IterableIterator<unknown> {
const results = []
for (const item of toArray(toValue(arr))) {
for (const item of toArray(arr)) {
results.push(yield this.context._getFromScope(item, stringify(property), false))
}
return results
}

export function * sum (this: FilterImpl, arr: Scope[], property?: string): IterableIterator<unknown> {
let sum = 0
for (const item of toArray(toValue(arr))) {
for (const item of toArray(arr)) {
const data = Number(property ? yield this.context._getFromScope(item, stringify(property), false) : item)
sum += Number.isNaN(data) ? 0 : data
}
return sum
}

export function compact<T> (this: FilterImpl, arr: T[]) {
arr = toValue(arr)
return toArray(arr).filter(x => !isNil(toValue(x)))
}

export function concat<T1, T2> (v: T1[], arg: T2[] = []): (T1 | T2)[] {
v = toValue(v)
arg = toArray(arg).map(v => toValue(v))
return toArray(v).concat(arg)
return toArray(v).concat(toArray(arg))
}

export function push<T> (v: T[], arg: T): T[] {
return concat(v, [arg])
}

export function unshift<T> (v: T[], arg: T): T[] {
const clone = [...toArray(v)]
clone.unshift(arg)
return clone
}

export function pop<T> (v: T[]): T[] {
const clone = [...toArray(v)]
clone.pop()
return clone
}

export function shift<T> (v: T[]): T[] {
const clone = [...toArray(v)]
clone.shift()
return clone
}

export function slice<T> (v: T[] | string, begin: number, length = 1): T[] | string {
v = toValue(v)
if (isNil(v)) return []
Expand All @@ -77,7 +91,7 @@ export function slice<T> (v: T[] | string, begin: number, length = 1): T[] | str

export function * where<T extends object> (this: FilterImpl, arr: T[], property: string, expected?: any): IterableIterator<unknown> {
const values: unknown[] = []
arr = toArray(toValue(arr))
arr = toArray(arr)
for (const item of arr) {
values.push(yield this.context._getFromScope(item, stringify(property).split('.'), false))
}
Expand Down
2 changes: 1 addition & 1 deletion src/tags/for.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Hash, ValueToken, Liquid, Tag, evalToken, Emitter, TagToken, TopLevelToken, Context, Template, ParseStream } from '..'
import { toEnumerable } from '../util/collection'
import { toEnumerable } from '../util'
import { ForloopDrop } from '../drop/forloop-drop'

const MODIFIERS = ['offset', 'limit', 'reversed']
Expand Down
2 changes: 1 addition & 1 deletion src/tags/tablerow.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { toEnumerable } from '../util/collection'
import { toEnumerable } from '../util'
import { ValueToken, Liquid, Tag, evalToken, Emitter, Hash, TagToken, TopLevelToken, Context, Template, ParseStream } from '..'
import { TablerowloopDrop } from '../drop/tablerowloop-drop'

Expand Down
16 changes: 0 additions & 16 deletions src/util/collection.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export * from './underscore'
export * from './operator-trie'
export * from './type-guards'
export * from './async'
export * from './collection'
export * from './strftime'
export * from './liquid-date'
export * from './timezone-date'
16 changes: 16 additions & 0 deletions src/util/underscore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ export function stringify (value: any): string {
return String(value)
}

export function toEnumerable<T = unknown> (val: any): T[] {
val = toValue(val)
if (isArray(val)) return val
if (isString(val) && val.length > 0) return [val] as unknown as T[]
if (isIterable(val)) return Array.from(val)
if (isObject(val)) return Object.keys(val).map((key) => [key, val[key]]) as unknown as T[]
return []
}

export function toArray (val: any) {
val = toValue(val)
if (isNil(val)) return []
if (isArray(val)) return val
return [ val ]
}

export function toValue (value: any): any {
return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value
}
Expand Down
51 changes: 47 additions & 4 deletions test/integration/filters/array.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,53 @@ describe('filters/array', function () {
await test('{{ undefinedValue | push: arg | join: "," }}', scope, 'foo')
await test('{{ nullValue | push: arg | join: "," }}', scope, 'foo')
})
it('should ignore nil right value', async () => {
const scope = { nullValue: null }
await test('{{ nullValue | push | join: "," }}', scope, '')
await test('{{ nullValue | push: nil | join: "," }}', scope, '')
it('should support nil right value', async () => {
const scope = { nullValue: [] }
await test('{{ nullValue | push | size }}', scope, '1')
await test('{{ nullValue | push: nil | size }}', scope, '1')
})
})

describe('pop', () => {
it('should support pop', async () => {
const scope = { val: ['hey', 'you'] }
await test('{{ val | pop | join: "," }}', scope, 'hey')
})
it('should not change original array', async () => {
const scope = { val: ['hey', 'you'] }
await test('{{ val | pop | join: "," }} {{ val| join: "," }}', scope, 'hey hey,you')
})
it('should support nil left value', async () => {
await test('{{ notDefined | pop | join: "," }}', {}, '')
})
})

describe('unshift', () => {
it('should support unshift', async () => {
const scope = { val: ['you'] }
await test('{{ val | unshift: "hey" | join: ", " }}', scope, 'hey, you')
})
it('should not change original array', async () => {
const scope = { val: ['you'] }
await test('{{ val | unshift: "hey" | join: "," }} {{ val | join: "," }}', scope, 'hey,you you')
})
it('should support nil right value', async () => {
const scope = { val: [] }
await test('{{ val | unshift: nil | size }}', scope, '1')
})
})

describe('shift', () => {
it('should support pop', async () => {
const scope = { val: ['hey', 'you'] }
await test('{{ val | shift }}', scope, 'you')
})
it('should not change original array', async () => {
const scope = { val: ['hey', 'you'] }
await test('{{ val | shift }} {{ val | join: ","}}', scope, 'you hey,you')
})
it('should support nil left value', async () => {
await test('{{ notDefined | pop }}', {}, '')
})
})

Expand Down
Loading

0 comments on commit 258780e

Please sign in to comment.