Merge pull request #175 from insanum/formulas

added some new formula functions
This commit is contained in:
Dhruva Sagar
2020-04-19 21:55:36 +05:30
committed by GitHub
5 changed files with 275 additions and 4 deletions

View File

@@ -1,4 +1,106 @@
" Private Functions {{{1 " Private Functions {{{1
function! s:TotalCells(list) "{{{2
let result = 0
for item in a:list
if type(item) == type([])
let result += s:TotalCells(item)
else
let result += 1
endif
endfor
return result
endfunction
function! s:Min(list) "{{{2
let found = v:false
let result = 0
for item in a:list
if empty(item)
continue
endif
if type(item) == type(1) || type(item) == type(1.0)
if found == v:false || item < result
let found = v:true
let result = item
endif
elseif type(item) == type('')
let val = str2float(item)
if found == v:false || val < result
let found = v:true
let result = val
endif
elseif type(item) == type([])
let val = s:Min(item)
if found == v:false || val < result
let found = v:true
let result = val
endif
endif
endfor
return result
endfunction
function! s:Max(list) "{{{2
let found = v:false
let result = 0
for item in a:list
if empty(item)
continue
endif
if type(item) == type(1) || type(item) == type(1.0)
if found == v:false || item > result
let found = v:true
let result = item
endif
elseif type(item) == type('')
let val = str2float(item)
if found == v:false || val > result
let found = v:true
let result = val
endif
elseif type(item) == type([])
let val = s:Max(item)
if found == v:false || val > result
let found = v:true
let result = val
endif
endif
endfor
return result
endfunction
function! s:CountE(list) "{{{2
let result = 0
for item in a:list
if empty(item)
let result += 1
elseif type(item) == type([])
let result += s:CountE(item)
endif
endfor
return result
endfunction
function! s:CountNE(list) "{{{2
let result = 0
for item in a:list
if type(item) == type([])
let result += s:CountNE(item)
elseif !empty(item)
let result += 1
endif
endfor
return result
endfunction
function! s:PercentE(list) "{{{2
return (s:CountE(a:list)*100)/s:TotalCells(a:list)
endfunction
function! s:PercentNE(list) "{{{2
return (s:CountNE(a:list)*100)/s:TotalCells(a:list)
endfunction
function! s:Sum(list) "{{{2 function! s:Sum(list) "{{{2
let result = 0.0 let result = 0.0
for item in a:list for item in a:list
@@ -14,7 +116,11 @@ function! s:Sum(list) "{{{2
endfunction endfunction
function! s:Average(list) "{{{2 function! s:Average(list) "{{{2
return s:Sum(a:list)/len(a:list) return s:Sum(a:list)/s:TotalCells(a:list)
endfunction
function! s:AverageNE(list) "{{{2
return s:Sum(a:list)/s:CountNE(a:list)
endfunction endfunction
" Public Functions {{{1 " Public Functions {{{1
@@ -244,6 +350,42 @@ function! tablemode#spreadsheet#InsertColumn(after) "{{{2
endif endif
endfunction endfunction
function! tablemode#spreadsheet#Min(range, ...) abort "{{{2
let args = copy(a:000)
call insert(args, a:range)
return s:Min(call('tablemode#spreadsheet#cell#GetCellRange', args))
endfunction
function! tablemode#spreadsheet#Max(range, ...) abort "{{{2
let args = copy(a:000)
call insert(args, a:range)
return s:Max(call('tablemode#spreadsheet#cell#GetCellRange', args))
endfunction
function! tablemode#spreadsheet#CountE(range, ...) abort "{{{2
let args = copy(a:000)
call insert(args, a:range)
return s:CountE(call('tablemode#spreadsheet#cell#GetCellRange', args))
endfunction
function! tablemode#spreadsheet#CountNE(range, ...) abort "{{{2
let args = copy(a:000)
call insert(args, a:range)
return s:CountNE(call('tablemode#spreadsheet#cell#GetCellRange', args))
endfunction
function! tablemode#spreadsheet#PercentE(range, ...) abort "{{{2
let args = copy(a:000)
call insert(args, a:range)
return s:PercentE(call('tablemode#spreadsheet#cell#GetCellRange', args))
endfunction
function! tablemode#spreadsheet#PercentNE(range, ...) abort "{{{2
let args = copy(a:000)
call insert(args, a:range)
return s:PercentNE(call('tablemode#spreadsheet#cell#GetCellRange', args))
endfunction
function! tablemode#spreadsheet#Sum(range, ...) abort "{{{2 function! tablemode#spreadsheet#Sum(range, ...) abort "{{{2
let args = copy(a:000) let args = copy(a:000)
call insert(args, a:range) call insert(args, a:range)
@@ -256,6 +398,12 @@ function! tablemode#spreadsheet#Average(range, ...) abort "{{{2
return s:Average(call('tablemode#spreadsheet#cell#GetCellRange', args)) return s:Average(call('tablemode#spreadsheet#cell#GetCellRange', args))
endfunction endfunction
function! tablemode#spreadsheet#AverageNE(range, ...) abort "{{{2
let args = copy(a:000)
call insert(args, a:range)
return s:AverageNE(call('tablemode#spreadsheet#cell#GetCellRange', args))
endfunction
function! tablemode#spreadsheet#Sort(bang, ...) range "{{{2 function! tablemode#spreadsheet#Sort(bang, ...) range "{{{2
if exists('*getcurpos') if exists('*getcurpos')
let col = getcurpos()[4] " curswant let col = getcurpos()[4] " curswant

View File

@@ -1,4 +1,8 @@
" Private Functions {{{1 " Private Functions {{{1
function! s:IsHTMLComment(line) "{{{2
return getline(a:line) =~# '^\s*<!--'
endfunction
function! s:IsFormulaLine(line) "{{{2 function! s:IsFormulaLine(line) "{{{2
return getline(a:line) =~# 'tmf: ' return getline(a:line) =~# 'tmf: '
endfunction endfunction
@@ -15,6 +19,7 @@ function! tablemode#spreadsheet#formula#Add(...) "{{{2
let fr = '$' . row . ',' . colm . '=' . fr let fr = '$' . row . ',' . colm . '=' . fr
let fline = tablemode#spreadsheet#GetLastRow('.') + 1 let fline = tablemode#spreadsheet#GetLastRow('.') + 1
if tablemode#table#IsBorder(fline) | let fline += 1 | endif if tablemode#table#IsBorder(fline) | let fline += 1 | endif
if s:IsHTMLComment(fline) | let fline += 1 | endif
let cursor_pos = [line('.'), col('.')] let cursor_pos = [line('.'), col('.')]
if getline(fline) =~# 'tmf: ' if getline(fline) =~# 'tmf: '
" Comment line correctly " Comment line correctly
@@ -55,6 +60,30 @@ function! tablemode#spreadsheet#formula#EvaluateExpr(expr, line) abort "{{{2
let [row, colm] = [0, str2nr(cell)] let [row, colm] = [0, str2nr(cell)]
endif endif
if expr =~# 'Min(.*)'
let expr = substitute(expr, 'Min(\([^)]*\))', 'tablemode#spreadsheet#Min("\1",'.line.','.colm.')', 'g')
endif
if expr =~# 'Max(.*)'
let expr = substitute(expr, 'Max(\([^)]*\))', 'tablemode#spreadsheet#Max("\1",'.line.','.colm.')', 'g')
endif
if expr =~# 'CountE(.*)'
let expr = substitute(expr, 'CountE(\([^)]*\))', 'tablemode#spreadsheet#CountE("\1",'.line.','.colm.')', 'g')
endif
if expr =~# 'CountNE(.*)'
let expr = substitute(expr, 'CountNE(\([^)]*\))', 'tablemode#spreadsheet#CountNE("\1",'.line.','.colm.')', 'g')
endif
if expr =~# 'PercentE(.*)'
let expr = substitute(expr, 'PercentE(\([^)]*\))', 'tablemode#spreadsheet#PercentE("\1",'.line.','.colm.')', 'g')
endif
if expr =~# 'PercentNE(.*)'
let expr = substitute(expr, 'PercentNE(\([^)]*\))', 'tablemode#spreadsheet#PercentNE("\1",'.line.','.colm.')', 'g')
endif
if expr =~# 'Sum(.*)' if expr =~# 'Sum(.*)'
let expr = substitute(expr, 'Sum(\([^)]*\))', 'tablemode#spreadsheet#Sum("\1",'.line.','.colm.')', 'g') let expr = substitute(expr, 'Sum(\([^)]*\))', 'tablemode#spreadsheet#Sum("\1",'.line.','.colm.')', 'g')
endif endif
@@ -63,6 +92,10 @@ function! tablemode#spreadsheet#formula#EvaluateExpr(expr, line) abort "{{{2
let expr = substitute(expr, 'Average(\([^)]*\))', 'tablemode#spreadsheet#Average("\1",'.line.','.colm.')', 'g') let expr = substitute(expr, 'Average(\([^)]*\))', 'tablemode#spreadsheet#Average("\1",'.line.','.colm.')', 'g')
endif endif
if expr =~# 'AverageNE(.*)'
let expr = substitute(expr, 'AverageNE(\([^)]*\))', 'tablemode#spreadsheet#AverageNE("\1",'.line.','.colm.')', 'g')
endif
if expr =~# '\$\-\?\d\+,\-\?\d\+' if expr =~# '\$\-\?\d\+,\-\?\d\+'
let expr = substitute(expr, '\$\(\-\?\d\+\),\(\-\?\d\+\)', let expr = substitute(expr, '\$\(\-\?\d\+\),\(\-\?\d\+\)',
\ '\=str2float(tablemode#spreadsheet#cell#GetCells(line, submatch(1), submatch(2)))', 'g') \ '\=str2float(tablemode#spreadsheet#cell#GetCells(line, submatch(1), submatch(2)))', 'g')
@@ -107,6 +140,7 @@ function! tablemode#spreadsheet#formula#EvaluateFormulaLine() abort "{{{2
if tablemode#table#IsRow('.') " We're inside the table if tablemode#table#IsRow('.') " We're inside the table
let line = tablemode#spreadsheet#GetLastRow('.') let line = tablemode#spreadsheet#GetLastRow('.')
let fline = line + 1 let fline = line + 1
if s:IsHTMLComment(fline) | let fline += 1 | endif
if tablemode#table#IsBorder(fline) | let fline += 1 | endif if tablemode#table#IsBorder(fline) | let fline += 1 | endif
while s:IsFormulaLine(fline) while s:IsFormulaLine(fline)
let exprs += split(matchstr(getline(fline), matchexpr), ';') let exprs += split(matchstr(getline(fline), matchexpr), ';')
@@ -116,6 +150,7 @@ function! tablemode#spreadsheet#formula#EvaluateFormulaLine() abort "{{{2
let fline = line('.') let fline = line('.')
let line = line('.') - 1 let line = line('.') - 1
while s:IsFormulaLine(line) | let fline = line | let line -= 1 | endwhile while s:IsFormulaLine(line) | let fline = line | let line -= 1 | endwhile
if s:IsHTMLComment(line) | let line -= 1 | endif
if tablemode#table#IsBorder(line) | let line -= 1 | endif if tablemode#table#IsBorder(line) | let line -= 1 | endif
if tablemode#table#IsRow(line) if tablemode#table#IsRow(line)
" let exprs = split(matchstr(getline('.'), matchexpr), ';') " let exprs = split(matchstr(getline('.'), matchexpr), ';')

View File

@@ -120,8 +120,11 @@ Formula Expressions :
The formula can be a simple mathematical expression involving cells The formula can be a simple mathematical expression involving cells
which are also defined by the same format as that of the target cell. which are also defined by the same format as that of the target cell.
Apart from basic mathematical expressions, table mode also provides Apart from basic mathematical expressions, table mode also provides
special functions 'Sum' and 'Average'. Both these functions take a special functions 'Min', 'Max', 'CountE' (number of empty cells),
range as input. A range can be of two forms : 'CountNE' (number of non-empty cells), 'PercentE' (percent of empty
cells), 'PercentNE' (percent of non-empty cells), 'Sum', 'Average',
and 'AverageNE' (average over non-empty cells). All these functions
take a range as input. A range can be of two forms :
'n:m': This represents cells in the current column from row 'n:m': This represents cells in the current column from row
'n' through 'm'. If 'm' is negative it represents 'm' row 'n' through 'm'. If 'm' is negative it represents 'm' row

View File

@@ -0,0 +1,3 @@
| 1 | 2 | 3 |
| | 0 | |
| 4 | 5 | 6 |

View File

@@ -74,11 +74,93 @@ describe 'spreadsheet'
call cursor(1,3) call cursor(1,3)
Expect tablemode#spreadsheet#Average('1:2') == 2.0 Expect tablemode#spreadsheet#Average('1:2') == 2.0
Expect tablemode#spreadsheet#Average('1,1:1,2') == 1.5 Expect tablemode#spreadsheet#Average('1,1:1,2') == 1.5
Expect tablemode#spreadsheet#Average('1,1:2,2') == 5.0 Expect tablemode#spreadsheet#Average('1,1:2,2') == 2.5
call cursor(2,7) call cursor(2,7)
Expect tablemode#spreadsheet#Average('1:2') == 3.0 Expect tablemode#spreadsheet#Average('1:2') == 3.0
Expect tablemode#spreadsheet#Average('2,1:2,2') == 3.5 Expect tablemode#spreadsheet#Average('2,1:2,2') == 3.5
end end
it 'should return the min of cell range'
call cursor(1,3)
Expect tablemode#spreadsheet#Min('1:2') == 1.0
Expect tablemode#spreadsheet#Min('1,1:1,2') == 1.0
Expect tablemode#spreadsheet#Min('1,1:2,2') == 1.0
call cursor(2,7)
Expect tablemode#spreadsheet#Min('1:2') == 2.0
Expect tablemode#spreadsheet#Min('2,1:2,2') == 3.0
end
it 'should return the max of cell range'
call cursor(1,3)
Expect tablemode#spreadsheet#Max('1:2') == 3.0
Expect tablemode#spreadsheet#Max('1,1:1,2') == 2.0
Expect tablemode#spreadsheet#Max('1,1:2,2') == 4.0
call cursor(2,7)
Expect tablemode#spreadsheet#Max('1:2') == 4.0
Expect tablemode#spreadsheet#Max('2,1:2,2') == 4.0
end
end
describe 'Count'
before
new
read t/fixtures/cell/counts.txt
end
it 'should return the count of empty cell range'
call cursor(1,3)
Expect tablemode#spreadsheet#CountE('1:3') == 1
Expect tablemode#spreadsheet#CountE('1,1:1,3') == 0
Expect tablemode#spreadsheet#CountE('2,1:2,3') == 2
Expect tablemode#spreadsheet#CountE('1,1:3,3') == 2
call cursor(3,11)
Expect tablemode#spreadsheet#CountE('1:3') == 1
Expect tablemode#spreadsheet#CountE('3,1:3,3') == 0
end
it 'should return the count of not-empty cell range'
call cursor(1,3)
Expect tablemode#spreadsheet#CountNE('1:3') == 2
Expect tablemode#spreadsheet#CountNE('1,1:1,3') == 3
Expect tablemode#spreadsheet#CountNE('2,1:2,3') == 1
Expect tablemode#spreadsheet#CountNE('1,1:3,3') == 7
call cursor(3,11)
Expect tablemode#spreadsheet#CountNE('1:3') == 2
Expect tablemode#spreadsheet#CountNE('3,1:3,3') == 3
end
it 'should return the percent count of empty cell range'
call cursor(1,3)
Expect tablemode#spreadsheet#PercentE('1:3') == 33
Expect tablemode#spreadsheet#PercentE('1,1:1,3') == 0
Expect tablemode#spreadsheet#PercentE('2,1:2,3') == 66
Expect tablemode#spreadsheet#PercentE('1,1:3,3') == 22
call cursor(3,11)
Expect tablemode#spreadsheet#PercentE('1:3') == 33
Expect tablemode#spreadsheet#PercentE('3,1:3,3') == 0
end
it 'should return the percent count of not-empty cell range'
call cursor(1,3)
Expect tablemode#spreadsheet#PercentNE('1:3') == 66
Expect tablemode#spreadsheet#PercentNE('1,1:1,3') == 100
Expect tablemode#spreadsheet#PercentNE('2,1:2,3') == 33
Expect tablemode#spreadsheet#PercentNE('1,1:3,3') == 77
call cursor(3,11)
Expect tablemode#spreadsheet#PercentNE('1:3') == 66
Expect tablemode#spreadsheet#PercentNE('3,1:3,3') == 100
end
it 'should return the average of not-empty cell range'
call cursor(1,3)
Expect tablemode#spreadsheet#AverageNE('1:3') == 2.5
Expect tablemode#spreadsheet#AverageNE('1,1:1,3') == 2.0
Expect tablemode#spreadsheet#AverageNE('2,1:2,3') == 0.0
Expect tablemode#spreadsheet#AverageNE('1,1:3,3') == 3.0
call cursor(3,11)
Expect tablemode#spreadsheet#AverageNE('1:3') == 4.5
Expect tablemode#spreadsheet#AverageNE('3,1:3,3') == 5.0
end
end end
end end