Cepat menghitung total kolom angka

15

Saya menuliskan tabel penurunan harga yang terlihat seperti ini:

| 13/05/15 | 09:30-16:00 |  6.5 |
| 14/05/15 | 10:00-16:30 |  6.5 |
| 16/05/15 | 15:30-01:00 |  9.5 |
| 21/05/15 | 09:00-16:30 |  7.5 |
| 22/05/15 | 08:30-17:00 |  8.5 |
| 28/05/15 | 09:30-15:30 |  6   |
| 02/06/15 | 09:00-20:00 | 11   |
| 03/06/15 | 08:30-22:30 | 14   |

Saya mencari cara untuk dengan cepat menghitung total kolom ketiga dan memasukkannya ke dalam buffer. Solusi yang ada dalam pikiran saya akan menggunakan mode blok visual (untuk memilih semua angka) dan mungkin dari daftar ekspresi (untuk melakukan matematika).

Apakah ini mungkin menggunakan perintah Vim asli? Jika tidak, apakah ada plugin yang dapat membantu saya?

zool
sumber
1
Anda dapat melihat artikel ini: vim.wikia.com/wiki/Using_vim_as_calculator
nobe4

Jawaban:

15

Saya menulis sebuah plugin: https://github.com/sk1418/HowMuch yang mendukung pemilihan visual dan melakukan perhitungan matematika.

Secara default plugin mendukung tiga mesin evaluasi ekspresi matematika: Gnu bc, python, dan vimscript. Anda dapat melakukan perhitungan pada yang tertentu atau membiarkan plugin secara otomatis memilih satu untuk Anda.

Ini berfungsi dengan contoh Anda seperti ini:

masukkan deskripsi gambar di sini

Untuk detailnya, baca README di github.

Kent
sumber
Akan sangat membantu jika Anda memasukkan penekanan tombol yang diperlukan untuk memilih, menjumlahkan, dan memasukkan jawaban Anda.
pdoherty926
@ pdoherty926 For details please read the README on github.Bahkan jika saya meletakkan penekanan tombol yang saya tekan untuk masalah ini di sini, saya tidak melihat seberapa membantu itu bisa terjadi, itu hanya 3 atau 4 kombinasi tombol. Jika skrip saya benar-benar dibutuhkan oleh seseorang, dia akan tetap memeriksa detailnya.
Kent
12

Jika Anda tidak ingin menggunakan plugin atau drop ke skrip bash, Anda dapat melakukan sesuatu seperti berikut:

  • c-V {motions} "ay salin kolom ke "a
  • :let @a = substitute(@a, 'c-V c-J', '+', 'g') ganti baris baris baru dengan +
  • ic-R=c-Rajalankan yang diganti "amelalui register ekspresi

Atau: buat entri riwayat ekspresi dapat digunakan kembali untuk jumlah kolom selanjutnya

  • ctrl-V {motions} y masukkan kolom ke daftar yank ""
  • ictrl-R=eval(substitute(@", '\n', '+', 'g'))

Mengulangi untuk kolom lain:

  • ctrl-V {motion} y (tidak berubah)
  • ictrl-R=<CR>atau jika Anda melakukan sesuatu yang lain dengan register ekspresi, siklus sejarah dengan tombol panah atas (atau dengan ctrl-Pjika Anda memetakannya kembali):
    ictrl-R=<up>...<up><CR>
Hovercouch
sumber
1
Untuk beberapa alasan saya hanya berhasil menggunakan solusi Anda dengan tanda kutip ganda, "bukan tanda kutip tunggal 'pada substituteperintah. Apakah Anda tahu apakah ada alasan untuk itu?
vappolinario
@ vappolinario bekerja dua arah untuk saya, jadi saya khawatir saya tidak tahu, maaf.
Hovercouch
@Hovercouch Bisakah Anda menguraikan langkah ketiga? Bagaimana tepatnya, seseorang menjalankan penggantian melalui register ekspresi?
pdoherty926
Bagaimana dengan membuat peta: `nnoremap <cs>: s / $ / \ = eval (pengganti (@ 0, '[^ 0-9]', '+', 'g')) / <cr>`
SergioAraujo
9
:r!awk '{sum+=$6} END {print "Total: "sum}' %

Penjelasan:

:r ........... read (put result in this file)
! ............ external command
awk .......... external tool
{sum+=$6} .... sixth field (awk considers spaces as field separator)
END .......... at the end
{print "Total: "sum} --> string "Total: " plus your result
% ............ current file

Saya telah mencoba fungsi yang berfungsi di sini:

" This function requires you select the numbers
fun! SumVis()
    try
        let l:a_save = @a
        norm! gv"ay
        let @a = substitute(@a,'[^0-9. ]','+','g')
        exec "norm! '>o"
        exec "norm! iTotal \<c-r>=\<c-r>a\<cr>"
     finally
        let @a = l:a_save
     endtry
endfun
vnoremap <leader>s :<C-u>call SumVis()<cr>

Menggunakan peta di atas termasuk, yang harus Anda lakukan setelah memuat dalam fungsi ini adalah memilih angka yang ingin Anda jumlahkan dan gunakan <leader>suntuk menjumlahkan area yang dipilih.

Penjelasan fungsi:

Ini menggunakan try/finally/endtryekstruktur untuk menangkap kesalahan.

let l:a_save = @a .......... if whe have register 'a' we save it temporarelly
norm! gv"a  ................................... gv --> reselects and captures selection to 'register a'
let @a = substitute(@a,'[^0-9. ]','+','g') .... removes all but numbers, dots and spaces from 'register a' and puts '+' among the numbers
exec "norm! '>o"  ............................. opens new line bellow selection. see :h '>
exec "norm! iTotal: \<c-r>=\<c-r>a\<cr>" ...... insert "Total: " plus 'expression register result
let @a = l:a_save ............................. restores original 'a' register content

Jika Anda ingin mencoba fungsi ini, lakukan hal berikut: Salin fungsi ini di browser Anda dan jalankan perintah ini pada vim :@+ ini akan memungkinkan Anda untuk menggunakan secara :call SumVis()normal.

:@+ ......... loads `+` register making the function avaiable

Perlu Anda membuat pilihan blok visual dengan ctrl+ v, batalkan pilihan dan akhirnya memanggil fungsi. Atau Anda dapat menggunakan peta yang disarankan yang dengan sendirinya menghapus pilihan sebelum menghitung.

SergioAraujo
sumber
7

Plugin csv saya memungkinkan ini. Gunakan :SumColperintah dan pastikan untuk membaca dokumentasi.

Christian Brabandt
sumber
5

Membuat plugin atau coding ini dalam vimscript agak berat. Saya percaya pada vim bebas plugin, dan komposisi yang baik dengan alat eksternal.

Ini adalah perintah 1 kali, berdasarkan pada user2571881, yang berfungsi bahkan jika buffer belum disimpan.

:%!awk -F '|' '{print; sum+=$4}; END {print "Total: "sum}'

Jika Anda ingin menyimpan perintah ini untuk penggunaan di masa mendatang, Anda mungkin ingin memberi nama:

:command! -range=% -nargs=1 SumColumn <line1>,<line2>!awk -F '|' '{print; sum+=$('<args>' + 1)} END {print "Total: "sum}'

Ini bekerja dengan pemilihan visual. Jika Anda memilih beberapa baris dan masuk ke mode perintah, vim akan mengawali perintah Anda dengan :'<,'>, yang merupakan rentang garis untuk pemilihan visual. Jadi Anda dapat menjalankan:

:'<,'>SumColumn 3

dan itu hanya akan menjumlahkan kolom ke-3 dari baris yang dipilih. Secara default kisarannya adalah %, jadi

:SumColumn 3

akan menjumlahkan kolom ke-3 dari semua baris.

EDIT: Jika Anda ingin dapat menentukan pemisah bidang lainnya dan default kolom dihitung ke yang terakhir, Anda dapat menutup perintah di bashdan menangani argumen dengan itu, seperti ini:

:command! -range=% -nargs=* SumColumn <line1>,<line2>!bash -c 'awk -F ${2:-|} "{print; sum+=\$(${1:-NF - 2} + 1)} END {print \"Total: \"sum}"' sumcolumn <args>

Sekarang,

:SumColumn

akan menghitung kolom terakhir dari tabel dengan "|" pemisah lapangan,

:SumColumn 3

akan menghitung kolom ke-3 tabel dengan "|" pemisah bidang, dan

:SumColumn 3 +

akan menghitung kolom ke-3 tabel dengan pemisah bidang "+".

JoL
sumber
Bagaimana seseorang dapat menangani pemisah lapangan lainnya yang mungkin? Hanya untuk membuat solusi lebih umum.
SergioAraujo
@ user2571881, saya sudah mengedit jawabannya, menunjukkan itu.
JoL
@ JoL menambahkan fungsi seperti SumColumnke vimrc berarti Anda cukup memiliki 'plugins' di vimrc Anda. Semoga Anda pandai mempertahankan ini seiring waktu. Bagi saya plugin menyediakan dokumentasi, pemisahan menjadi bagian-bagian yang bermakna, memanfaatkan kecerdikan orang lain. Saya berkontribusi pada upstream yang meningkatkan plugin luar biasa yang tidak ada yang punya waktu untuk membuat semuanya sendiri (kecuali tpope). Apakah Anda tidak menggunakan vim-surround, vim-fugitive, vim-easy-align / vim-lion, vim-unimpaired, vim-commentary, ultisnips atau ft-specific yang seperti vim-go, vim-rails, vimtex?
Hotschke
@ Hotschke Ketika saya tiba di sini, saya melihat pertanyaan dan berpikir, "baiklah, coba saja. Tapi kemudian, saya melihat jawaban yang diterima adalah, "hei, unduh ratusan plugin LOC ini dan instal." Jawaban ketiga adalah, "hei, unduh ribuan plugin LOC ini dan instal." Ini berlebihan dan mengasapi. Bahkan jika Anda perlu menjumlahkan kolom lebih dari satu kali dalam hidup Anda, itu berlebihan. Jawaban saya dimaksudkan untuk menunjukkan bagaimana Anda dapat melakukan ini dalam satu perintah tanpa-plugin, tanpa-omong kosong jika Anda hanya perlu melakukan ini satu kali, dan bagaimana Anda dapat membuat perintah sederhana dengan parameter di luarnya jika Anda perlu melakukan ini sering.
JoL
@ Hotschke Untuk menjawab pertanyaan Anda, saya biasanya memasang setiap plugin di bawah matahari yang tampak sangat keren, tetapi kemudian vim saya sangat lambat (baca "sedikit lamban" yang tidak dapat ditoleransi untuk editor). Saat melihat lebih dalam vim docs, saya menyadari bahwa saya tidak benar-benar membutuhkan plugin. Banyak fitur stok yang cukup baik, dan, bagi mereka yang tidak memiliki, shell adalah cara untuk pergi. Secara mendasar (mengabaikan pengecualian yang dibuatnya), sesuai dengan filosofi Unix, vim adalah editor yang dapat beroperasi dengan baik dengan perangkat OS lainnya. Saya percaya itulah cara terbaik untuk memanfaatkannya. Tidak ada plugin sejak itu.
JoL
2

Jika kolom disejajarkan dengan benar, ini dapat dilakukan dengan oneliner sederhana.

  1. pertama pilih kolom dalam mode visual blok-bijaksana sebagai jawaban lain telah menunjukkan -> CTRL-V+ memindahkan kursor
  2. tarik pilihan dengan y
  3. ketik: :echo eval(join(split(@", '\_s\+'), '+'))yang membagi teks yang ditarik ke spasi dan baris baru, bergabung kembali dengan elemen +karakter, dan mengevaluasi string.
  4. Cara lain untuk melanjutkan: mengganti baris baru dengan +dan mengevaluasi: :echo eval(substitute(@", "\n", '+', 'g'))- eval()adalah hal terdekat yang reducekita miliki.

Jika tidak, Anda harus menggunakan trik lain untuk menghitung bidang. Misalnya, split(getline('.'), "[ \t|]\\+")dapat digunakan untuk memisahkan kolom dari satu baris dalam array Anda. Dari sana, menjadi sesederhana:

  1. pilih garis Anda dalam mode visual
  2. :echo eval(join(map(getline("'<", "'>"), { -> split(v:val, "[ \t|]\\+")[2] }), '+'))

Untuk menghilangkan nilai magis (bidang nomor - 1, dan +), ini bisa menjadi perintah

:command! -range=% -nargs=+ OnField 
    \ echo { field, what -> eval(join(map(getline(<line1>, <line2>), { -> split(v:val, "[ \t|]\\+")[field-1] }), what))}(<f-args>)

Yang dapat digunakan dengan:

:OnField  3 +
:2,5OnField  3 +
:'<,'>Onfield 3 *   " after line-wise selection
....

Catatan: Di sini saya menggunakan lambdas dari Vim 7.4.1xxx

Luc Hermitte
sumber
1

vmap ++dari plugin vmatholeh Damian Conway

  1. Instal plugin dari github (hanya 178 sloc) misalnya

    $ wget https://raw.githubusercontent.com/thoughtstream/Damian-Conway-s-Vim-Setup/master/plugin/vmath.vim -P ~/.vim/pack/manual/start/damians-tools/plugin
    
  2. Tambahkan pemetaan ke vimrc Anda

    vmap <silent><expr>  ++  VMATH_YankAndAnalyse()
    

    Namun, saya akan menyarankan untuk menggunakan sesuatu yang lain, misalnya gA

  3. Pindah ke kolom ketiga 2f|dan pilih kolom dalam mode blok visual<C-V>G$
  4. Tekan ++(atau pemetaan yang Anda pilih)
  5. Hasil ditampilkan dan disimpan dalam register (jumlah s)
  6. Masukkan jumlah dari register s, misalnya dengan"sp

Untuk presentasi plugin ini, lihat video YouTube Damian Conway, "More Instantly Better Vim" - OSCON 2013 (mulai dari menit 29).

Hotschke
sumber
1

Alat cli eksternal csvstatdari csvkit

:!csvstat -d '|' -H -c 4 --sum %
69.5

Penjelasan singkat tentang opsi

  • -d DELIMITERMembatasi karakter file CSV input. Di sini |.
  • -H Tentukan bahwa file CSV input tidak memiliki baris header.
  • -c COLUMNSDaftar indeks atau nama kolom yang dipisahkan koma untuk diperiksa. Default untuk semua kolom.
  • --sum Hanya jumlah output.

Alat ini juga menyediakan min, maks, rata-rata, median, stdev (standar deviasi), menghitung nilai unik, daftar nilai sering.

Masukkan ke dalam file dengan

<C-r>=system("csvstat -d '|' -H -c 4 --sum FILENAME 2> /dev/null")  

Instalasi

Pada macOS csvkit tersedia melalui homebrew dan pada Debian / Ubuntu dan sejenisnya dapat diinstal dengan $ sudo apt install csvkit.

Hotschke
sumber