In JavaScript, the Array.map() function exists such that
const array1 = [1, 4, 9, 16];
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
I need a bash equivalent where I can take my array, manipulate its contents, then receive a new array with the manipulations.
array1=(1 4 9 16)
map1=# ????
echo ${map1[*]}
Soooooooo, just write the loop.
array1=(1 4 9 16)
map1=()
for i in "${array1[#]}"; do
map1+=("$((i * 2))")
done
echo "${map1[#]}"
Might be a good time to re-read an introduction to Bash arrays.
It is possible to implement an array_walk with a callback to perform operation on each element this way:
#!/usr/bin/env bash
# Applies the user-defined callback function to each element of the array.
#
# #params
# $1: The array name to walk
# $2: The callback command or function name
array_walk() {
local -n __array=$1
local -- __callback=$2 __i
for __i in "${!__array[#]}"; do
"$__callback" "$1[$__i]"
done
}
x2() {
local -n __e=$1
__e=$((2 * __e))
}
array1=(1 4 9 16)
array_walk array1 x2
printf '%s\n' "${array1[*]}"
Related
I need to filter the response from getWater and getSoda. The problem I have is when I try to get response in API I get both querys. So in cli lets say i put CLI:getWater the response it gives for both water and soda i need to distinguish between the two if you look at the end line it gives you 1 for Water and 0 for Soda. I'm trying to make filter in TCL file so if i put getWater it only pulls out the query with whatever ends with 1 and vice versa.
cli% getWater {2 Fiji - {} 1 {} b873-367ef9944d48 **1**} {3 Coke - {} 1 {} 9d39-56ad9be6ee9f **0**} {6 Dasani - {} 1 {} 9d39-56ad9be6ee9f **1**} {9 Fanta - {} 1 {} 9d39-56ad9be6ee9f **0**}
im having hard time coding it because in not familiar with TCL
but so far to get query i got this.
proc API::get {args} {
set argc [llength $args]
if {$argc == 1} {
# get all sets based on set type
set objtype [lindex $args 0]
catcher getset_int $objtype {} {}
I'm guessing that you have some command (that I'll call getListOfRecords for the sake of argument) and you want to filter the returned list by the value of the 8th element (index 7; TCL uses zero-based indexing) of each record? You can do that with either lmap+lindex or with lsearch (with the right options).
proc getRecordsOfType {typeCode} {
lmap r [getListOfRecords] {
if {[lindex $r 7] eq $typeCode} {set r} else continue
}
}
proc getRecordsOfType {typeCode} {
lsearch -all -inline -exact -index 7 [getListOfRecords] $typeCode
}
Using lsearch is probably faster, but the other approach is far more flexible. (Measure instead of guessing if it matters to you.)
getWater is just getRecordsOfType 1.
Is there any way to port the following python function to an ash-based shell script?
#def degrees_to_cardinal(d):
#dirs = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
#"S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"]
# ix = int((d + 11.25)/22.5)
#return dirs[ix % 16]
Essentially what I need is to convert a value corresponding to a direction (0-360 degrees) to its corresponding 16-point cardinal direction. degrees -> cardinal direction.
I could use a series of if elif elif... but that seems clumsy and inefficient.
It must be said that bash has no real way of handling floating point numbers.
Of course, to get around this you can just multiply 360 by 5 or 10 and then modify
the rest of the script accordingly.
Here's something that might suit your needs:
D=N..NNENE.ENEE..ESESE.SSES..SSWSW.WSWW..WNWNW.NNW
d=$((d*2))
n=${D:$(((d/45)*3)):3}
echo ${n//.*}
Where "d" is the degree value
cat degs2dir
#!/bin/ash
awk -v deg="$1" '
BEGIN {
dirs[1]="N" ; dirs[2]="NNE" ; dirs[3]="NE" ; dirs[4]="ENE"
dirs[5]= "E" ; dirs[6]="ESE" ; dirs[7]="SE" ; dirs[8]="SSE"
dirs[9]="S" ; dirs[10]="SSW"; dirs[11]="SW"; dirs[12]="WSW"
dirs[13]="W" ; dirs[14]="WNW"; dirs[15]="NW"; dirs[16]="NNW"
}
END {
ix = int((deg + 11.25)/22.5)
print dirs[ix]
}' /dev/null
chmod 755 degs2dir
degs2dir 237
#output
SW
IHTH
For instance, how can I construct a list consisting of all the digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9.
You can construct it with val xs = ($list {int} (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)).
Make sure you specify the correct memory allocation functions by passing -DATS_MEMALLOC_LIBC to the compiler when using this code.
If you compile to JavaScript via atscc2js, then you need to construct
the list in the following manner:
val ds =
0::1::2::3::4::5::6::7::8::9::nil{int}()
// end of [val]
It also works for targeting C.
There are also combinators for this sort of things. For instance,
val ds = (10).list_map(TYPE{int})(lam(i) => i)
val ds = list_tabulate_cloref<int>(10, lam i => i)
You can find special literal on emacs mode:
https://github.com/githwxi/ATS-Postiats/blob/653af81715cf6bfbd1d2cd5ece1e88c8c3912b4a/utils/emacs/ats2-mode.el#L287
(defvar ats-special-keywords
'("$arrpsz" "$arrptrsize" "$delay" "$ldelay" "$effmask" "$effmask_ntm" "$effmask_exn" "$effmask_ref"
"$effmask_wrt" "$effmask_all" "$extern" "$extkind" "$extype" "$extype_struct" "$extval" "$lst"
"$lst_t" "$lst_vt" "$list" "$list_t" "$list_vt" "$rec" "$rec_t" "$rec_vt"
"$record" "$record_t" "$record_vt" "$tup" "$tup_t" "$tup_vt" "$tuple" "$tuple_t"
"$tuple_vt" "$raise" "$showtype" "$myfilename" "$mylocation" "$myfunction" "#assert" "#define"
"#elif" "#elifdef" "#elifndef" "#else" "#endif" "#error" "#if" "#ifdef"
"#ifndef" "#print" "#then" "#undef" "#include" "#staload" "#dynload" "#require"))
If you find some keyword, you can easily know how to use it on doc/EXAMPLE/ directory:
$ git clone https://github.com/githwxi/ATS-Postiats.git
$ cd ATS-Postiats/doc/EXAMPLE
$ grep -r "\$list" . | head
./MISC/word-chain.dats: $list{word}("", "")
./MISC/word-chain.dats: $list{word}("", "")
./MISC/mysendmailist.dats:$list{string}
./MISC/monad_list.dats: $list{a}("this", "that", "a")
./MISC/monad_list.dats: $list{a}("frog", "elephant", "thing")
./MISC/monad_list.dats: $list{a}("walked", "treaded", "grows")
./MISC/monad_list.dats: $list{a}("slowly", "quickly")
./ATSLF/CoYonedaLemma.dats:val myintlist0 = g0ofg1($list{int0}(I(1), I(0), I(1), I(0), I(0)))
./ATSLF/YonedaLemma.dats: $list{bool}(True, False, True, False, False)
./ATS-QA-LIST/qa-list-2014-12-07.dats:$list{double}(0.111111, 0.222222, 0.333333)
For a list0-value, you can do
val xs = g0ofg1($list{T}(x1, ..., xn))
where T is the type for the elements in xs. For instance,
val some_int_list = g0ofg1($list{int}(0, 9, 8, 7, 3, 4))
How to get a list of members based on their ID from a sorted set instead of just one member?
I would like to build a subset with a set of IDs from the actual sorted set.
I am using a Ruby client for Redis and do not want to iterate one by one. Because there could more than 3000 members that I want to lookup.
Here is the issue tracker to a new command ZMSCORE to do bulk ZSCORE.
There is no variadic form for ZSCORE, yet - see the discussion at: https://github.com/antirez/redis/issues/2344
That said, and for the time being, what you could do is use a Lua script for that. For example:
local scores = {}
while #ARGV > 0 do
scores[#scores+1] = redis.call('ZSCORE', KEYS[1], table.remove(ARGV, 1))
end
return scores
Running this from the command line would look like:
$ redis-cli ZADD foo 1 a 2 b 3 c 4 d
(integer) 4
$ redis-cli --eval mzscore.lua foo , b d
1) "2"
2) "4"
EDIT: In Ruby, it would probably be something like the following, although you'd be better off using SCRIPT LOAD and EVALSHA and loading the script from an external file (instead of hardcoding it in the app):
require 'redis'
script = <<LUA
local scores = {}
while #ARGV > 0 do
scores[#scores+1] = redis.call('ZSCORE', KEYS[1], table.remove(ARGV, 1))
end
return scores
LUA
redis = ::Redis.new()
reply = redis.eval(script, ["foo"], ["b", "d"])
Lua script to get scores with member IDs:
local scores = {}
while #ARGV > 0 do
local member_id = table.remove(ARGV, 1)
local member_score = {}
member_score[1] = member_id
member_score[2] = redis.call('ZSCORE', KEYS[1], member_id)
scores[#scores + 1] = member_score
end
return scores
Edit - Answer posted below
I have a script that usually uses #ARGV arguments but in some cases it is invoked by another script (which I cannot modify) that instead only passes a config filename which among other things has the command line options that should have been passed directly.
Example:
Args=--test --pdf "C:\testing\my pdf files\test.pdf"
If possible I'd like a way to parse this string into an array that would be identical to #ARGV.
I have a workaround where I setup an external perl script that just echos #ARGV, and I invoke this script like below (standard boilerplate removed).
echo-args.pl
print join ("\n", #ARGV);
test-echo-args.pl
$my_args = '--test --pdf "C:\testing\my pdf files\test.pdf"';
#args = map { chomp ; $_ } `perl echo-args.pl $my_args`;
This seems inelegant but it works. Is there a better way without invoking a new process? I did try splitting and processing but there are some oddities on the command line e.g. -a"b c" becomes '-ab c' and -a"b"" becomes -ab" and I'd rather not worry about edge cases but I know that'll bite me one day if I don't.
Answer - thanks ikegami!
I've posted a working program below that uses Win32::API and CommandLineToArgvW from shell32.dll based on ikegami's advice. It is intentionally verbose in the hopes that it'll be more easy to follow for anyone like myself who is extremely rusty with C and pointer arithmetic.
Any tips are welcome, apart from the obvious simplifications :)
use strict;
use warnings;
use Encode qw( encode decode );
use Win32::API qw( );
use Data::Dumper;
# create a test argument string, with some variations, and pack it
# apparently an empty string returns $^X which is documented so check before calling
my $arg_string = '--test 33 -3-t" "es 33\t2 ';
my $packed_arg_string = encode('UTF-16le', $arg_string."\0");
# create a packed integer buffer for output
my $packed_argc_buf_ptr = pack('L', 0);
# create then call the function and get the result
my $func = Win32::API->new('shell32.dll', 'CommandLineToArgvW', 'PP', 'N')
or die $^E;
my $ret = $func->Call($packed_arg_string, $packed_argc_buf_ptr);
# unpack to get the number of parsed arguments
my $argc = unpack('L', $packed_argc_buf_ptr);
print "We parsed $argc arguments\n";
# parse the return value to get the actual strings
my #argv = decode_LPWSTR_array($ret, $argc);
print Dumper \#argv;
# try not to leak memory
my $local_free = Win32::API->new('kernel32.dll', 'LocalFree', 'N', '')
or die $^E;
$local_free->Call($ret);
exit;
sub decode_LPWSTR_array {
my ($ptr, $num) = #_;
return undef if !$ptr;
# $ptr is the memory location of the array of strings (i.e. more pointers)
# $num is how many we need to get
my #strings = ();
for (1 .. $num) {
# convert $ptr to a long, using that location read 4 bytes - this is the pointer to the next string
my $string_location = unpack('P4', pack('L', $ptr));
# make it human readable
my $readable_string_location = unpack('L', $string_location);
# decode the string and save it for later
push(#strings, decode_LPCWSTR($readable_string_location));
# our pointers are 32-bit
$ptr += 4;
}
return #strings;
}
# Copied from http://stackoverflow.com/questions/5529928/perl-win32api-and-pointers
sub decode_LPCWSTR {
my ($ptr) = #_;
return undef if !$ptr;
my $sW = '';
for (;;) {
my $chW = unpack('P2', pack('L', $ptr));
last if $chW eq "\0\0";
$sW .= $chW;
$ptr += 2;
}
return decode('UTF-16le', $sW);
}
In unix systems, it's the shell that parses that shell command into strings. But in Windows, it's up to each application. I think this is normally done using the CommandLineToArgv system call (which you could call with the help of Win32::API), but the spec is documented here if you want to reimplement it yourself.