Split an array based on value - powershell

This is my first question here, so sorry if I make any mistakes posting this.
I'm trying to split an array based on its values. Basically I want to create two arrays whose values are as close to the average as possible. I managed to do this with this code:
function Sum($v) {
[Linq.Enumerable]::Sum([int64[]]$v)
}
$arr = 0..9 | % {get-random -min 1 -max 10}
"ARRAY:" + $arr
"SUM: " + (sum $arr)
"AVG: " + (sum $arr)/2
# start of the code that matters
$wavg = (sum $arr)/2
foreach ($i in (0..($arr.Count-1))) {
$wavg -= $arr[$i]
if ($wavg -le 0) {
$i-=(-$wavg -gt $arr[$i]/2);break
}
}
"SPLIT INDEX: " + $i
"ARR1: " + $arr[0..$i] + " (" + $(sum $arr[0..$i]) + ")"
"ARR2: " + $arr[($i+1)..$arr.Count] + " (" + $(sum $arr[($i+1)..$arr.Count]) + ")"
The reason my foreach is structured this way is because in my actual code the values are in an index hash and are accessed as $index[$arr[$i]].
This means that the resulting two arrays could be of unequal size (it would be easy if I could just split the array in half). Sample output of my code to demonstrate this:
ARRAY: 5 3 6 3 2 3 6 3 1 3
SUM: 35
AVG: 17.5
SPLIT INDEX: 3
ARR1: 5 3 6 3 (17)
ARR2: 2 3 6 3 1 3 (18)
The code works as is, but I feel it could be done in a more elegant and speedier way. Because I need to execute this code a few thousand times in my script I want it to be as fast as possible.

Related

Find the number at the n position in the infinite sequence

Having an infinite sequence s = 1234567891011...
Let's find the number at the n position (n <= 10^18)
EX: n = 12 => 1; n = 15 => 2
import Foundation
func findNumber(n: Int) -> Character {
var i = 1
var z = ""
while i < n + 1 {
z.append(String(i))
i += 1
}
print(z)
return z[z.index(z.startIndex, offsetBy: n-1)]
}
print(findNumber(n: 12))
That's my code but when I find the number at 100.000th position, it returns an error, I thought I appended too many i to z string.
Can anyone help me, in swift language?
The problem we have here looks fairly straight forward. Take a list of all the number 1-infinity and concatenate them into a string. Then find the nth digit. Straight forward problem to understand. The issue that you are seeing though is that we do not have an infinite amount of memory nor time to be able to do this reasonably in a computer program. So we must find an alternative way around this that does not just add the numbers onto a string and then find the nth digit.
The first thing we can say is that we know what the entire list is. It will always be the same. So can we use any properties of this list to help us?
Let's call the input number n. This is the position of the digit that we want to find. Let's call the output digit d.
Well, first off, let's look at some examples.
We know all the single digit numbers are just in the same position as the number itself.
So, for n<10 ... d = n
What about for two digit numbers?
Well, we know that 10 starts at position 10. (Because there are 9 single digit numbers before it). 9 + 1 = 10
11 starts at position 12. Again, 9 single digits + one 2 digit number before it. 9 + 2 + 1 = 12
So how about, say... 25? Well that has 9 single digit numbers and 15 two digit numbers before it. So 25 starts at 9*1 + 15*2 + 1 = 40 (+ 1 as the sum gets us to the end of 24 not the start of 25).
So... 99 starts at? 9*1 + 89*2 + 1 = 188.
The we do the same for the three digit numbers...
100... 9*1 + 90*2 + 1 = 190
300... 9*1 + 90*2 + 199*3 + 1 = 787
1000...? 9*1 + 90*2 + 900*3 + 1 = 2890
OK... so now I'm seeing a pattern here that seems to need to know the number of digits in each number. Well... I can get the number of digits in a number by rounding up the log(base 10) of that number.
rounding up log base 10 of 5 = 1
rounding up log base 10 of 23 = 2
rounding up log base 10 of 99 = 2
rounding up log base 10 of 627 = 3
OK... so I think I need something like...
// in pseudo code
let lengthOfNumber = getLengthOfNumber(n)
var result = 0
for each i from 0 to lengthOfNumber - 1 {
result += 9 * 10^i * (i + 1) // this give 9*1 + 90*2 + 900*3 + ...
}
let remainder = n - 10^(lengthOfNumber - 1) // these have not been added in the loop above
result += remainder * lengthOfNumber
So, in the above pseudo code you can give it any number and it will return the position in the list that that number starts on.
This isn't the exact same as the problem you are trying to solve. And I don't want to solve it for you.
This is just a leg up on how I would go about solving it. Hopefully, this will give you some guidance on how you can take this further and solve the problem that you are trying to solve.

Separator for five or more digits number in Tableau

I was working on a Tableau Project. We want to have a separator for five or more digits numbers.
For ex:-
1 as 1
12 as 12
123 as 123
1234 as 1234
12345 as 12,345
123456 as 1,23,456
Can you please assist me, how to achieve this?
I am nearly sure that this cannot be done as long as numbers are formatted as numbers. However, as a workaround, I have developed a method which however will convert numbers to string. Let's say you have a column col of desired numbers
copy your column say col2 (save original for future use) and convery type to string
Create a new calculated field say desired by using this calculation
If LEN([Col2]) <= 4 THEN
[Col2]
ELSEIF LEN([Col2]) < 6 THEN
REPLACE([Col2], RIGHT([Col2], 3), "") + "," +RIGHT([Col2], 3)
ELSEIF LEN([Col2]) <8 THEN
REPLACE([Col2], RIGHT([Col2], 5), "") + "," +
REPLACE(RIGHT([Col2],5), RIGHT([Col2], 3), "") + "," +RIGHT([Col2], 3)
ELSE
REPLACE([Col2], RIGHT([Col2], 7), "") + "," +
REPLACE(RIGHT([Col2],7), RIGHT([Col2], 5), "") + "," +
REPLACE(RIGHT([Col2],5), RIGHT([Col2], 3), "") + "," +RIGHT([Col2], 3)
END
this CF will work exactly as desired for upto 9 digits.
Alignment is not a big problem, if considered

How do I filter a 'reduce inputs' over a large stream of objects?

I use this to accumulate a map of unique keys whose value is the aggregate count and duration totals. Currently it runs on every input via 'reduce inputs'.
reduce inputs as $r
({};
("Pipeline:" + $r.m."topic.type") as $topic
| ("Channel:" + $r.channel) as $channel
| ("Campaign:" + $r.campaign) as $campaign
| ("Cellcode:" + $r.cellcode) as $cellcode
| ("Tracking:" + $r.tracking) as $tracking
| ("Template:" + $r.m."template.id") as $template
| ("Event:" + $r.name) as $event
| ("Reason:" + $r.reason) as $reason
| ($r.duration|tonumber) as $duration
| (($topic + ":" + $channel + ":" + $campaign + ":" + $cellcode + ":" + $tracking + ":" + $template + ":" + $event + ":" + $reason) as $key
| .[$key][0] += 1 | .[$key][1] += $duration)
I cannot figure out where to put a select() filter so that I do my reduce across only those entries that pass a 'select($r.type == "AUDIT_CHANNEL")' check, in order to skip the 2 "type":"AUDIT_SYSTEM" events in this test data:
{"type":"AUDIT_CHANNEL","name":"DROPPED","reason":"INVALID_MAIL_META_DATA","start":"1472083067058","duration":"91","end":"1472083067149","dc":"dev","pool":"raptor-app","host.name":"L-SEA-10002721","host.ip":"10.236.67.80","rlogid":"tfsqiu.dvw9%3FJ*P%40G*25671246-156befd00b2-0x293","channel":"EMAIL","m":{"audited":"1472083067058","created":"1472083066974","enabled":"true","entity.common.version":"1","template.id":"2840df6d-d9e8-4f27-e8b5-918c122d4561","template.version":"17","topic.curname":"eddude-default-topic","topic.curtype":"DEFAULT","topic.dc":"LVS","topic.name":"eddude-default-topic","topic.part":"5","topic.type":"DEFAULT"},"id":"0AEC4350-1C6E2FC9B80-0156BEF9ED92-0000000000000003","campaign":"999","contract":"a5872a5c-8912-dd63-583f-61fa8db3efde","user":1276847275,"cellcode":"","age":"175"}
{"type":"AUDIT_SYSTEM","name":"ROTATED","start":"1472083081033","duration":"0","end":"1472083081033","dc":"dev","pool":"raptor-app","host.name":"L-SEA-10002721","host.ip":"10.236.67.80","rlogid":"tfsqiu.dvw9%3FJ*P%40G*25671246-156befd3749-0xce"}
{"type":"AUDIT_SYSTEM","name":"ROTATED","start":"1472083141034","duration":"0","end":"1472083141034","dc":"dev","pool":"raptor-app","host.name":"L-SEA-10002721","host.ip":"10.236.67.80","rlogid":"tfsqiu.dvw9%3FJ*P%40G*25671246-156befe21aa-0xce"}
{"type":"AUDIT_CHANNEL","name":"RECEIVED","start":"1472083158860","duration":"109","end":"1472083158969","dc":"dev","pool":"raptor-app","host.name":"L-SEA-10002721","host.ip":"10.236.67.80","rlogid":"tfsqiu.dvw9%3FJ*P%40G*25671246-156befe674c-0x10f","channel":"EMAIL","m":{"audited":"1472083158860","created":"1472083158860","enabled":"true","entity.common.version":"1","template.id":"2840df6d-d9e8-4f27-e8b5-918c122d4561","template.version":"17","topic.curname":"eddude-default-topic","topic.curtype":"DEFAULT","topic.dc":"LVS","topic.name":"eddude-default-topic","topic.part":"5","topic.type":"DEFAULT"},"id":"0AEC4350-1C6E2FC9B80-0156BEF9ED92-0000000000000004","campaign":"999","contract":"a5872a5c-8912-dd63-583f-61fa8db3efde","user":1276847275,"cellcode":"","age":"109"}
I tried putting it in front of the reduce, inside the reduce, etc but I don't get the desired output which is:
{
"Pipeline:DEFAULT:Channel:EMAIL:Campaign:999:Cellcode::Tracking::Template:2840df6d-d9e8-4f27-e8b5-918c122d4561:Event:DROPPED:Reason:INVALID_MAIL_META_DATA": [
1,
91
],
"Pipeline:DEFAULT:Channel:EMAIL:Campaign:999:Cellcode::Tracking::Template:2840df6d-d9e8-4f27-e8b5-918c122d4561:Event:RECEIVED:Reason:": [
1,
109
]
}
Do I have to perform filtering totally outside of the reduce run, or am I just not aware of how to do this with a single filter-and-reduce?
Btw, assume this input is a giant stream of millions of records, with a few hundred unique "keys" that get calculated for accumulating into.
inputs will produce a result for every input that it is fed. You want to filter those inputs by the type so you could put your filter there:
reduce (inputs | select(.type == "AUDIT_CHANNEL")) as $r ...
I would write your filter like so:
reduce (inputs | select(.type == "AUDIT_CHANNEL")) as $r ({};
([
"Pipeline", $r.m."topic.type",
"Channel", $r.channel,
"Campaign", $r.campaign,
"Cellcode", $r.cellcode,
"Tracking", $r.tracking,
"Template", $r.m."template.id",
"Event", $r.name,
"Reason", $r.reason
] | join(":")) as $key
| .[$key] |= [ .[0]+1, .[1]+($r.duration|tonumber) ]
)

How to create a script that would calculate the difference between samples in a list?

I am trying to create a script that would calculate the difference between samples in a list.
If we take this example:
- result1 = 33
- result2 = 45
- result3 = 66
- result4 = 47
- result"n" = 50
The calculus should start at the second result from the list and descend until the last result, and then sum up those results:
result2 - result1 = 12,
result3 - result2 = 21,
result4 - result3 = 19,
result"n" - result4= 3
sum = 12 + 21 + 19 + 3 = 55
I am new at scripting, and so far i only came up with this solution:
$numbers
$1=[math]::abs($numbers[0]-$numbers[1])
$2=[math]::abs($numbers[1]-$numbers[2])
$3=[math]::abs($numbers[2]-$numbers[3])
$4=[math]::abs($numbers[3]-$numbers[4])
write-host "the results = $1, $2, $3, $4"
$sum = $1 + $2 + $3 + $4
The problem is that the list is dynamic and changes in length, one time there are 10 results and one time 20 for example.
I found a similar question here, but i don't know how to implement the solution to my case, as that is too complicated for me.
What you need is a For loop. It is structured as such:
For(<initial declaration, usually a start point like $i = 0>; <Condition to stop when false>;<Action to perform on each iteration to progress loop>){
Code to perform on each loop
}
For you we would do something like:
For($i=1;$i -le $numbers.count;$i++)
That starts 1, and since arrays start at 0 this will get you going with the second record. Then in the scriptblock we do something like:
{
[array]$Results += [math]::abs($numbers[$i] - $numbers[($i-1)])
}
That will get the differences for you, then to display them you can do something like:
"the results = " + ($Results -join ", ")
$sum = $Results|Measure -sum|select -expand Sum
So you put that all together and get
For($i=1;$i -le $numbers.count;$i++){
[array]$Results += [math]::abs($numbers[$i] - $numbers[($i-1)])
}
"the results = " + ($Results -join ", ")
$sum = $Results|Measure -sum|select -expand Sum
Use a for loop, use the length of your $numbers array to know when to stop.
$numbers = #(33,45,66,47,50)
$sum = 0
for($cur=1;$cur -lt $numbers.Length; $cur += 1){
$sum += [math]::abs($numbers[$cur]-$numbers[$cur-1]);
}
$sum

Mapping binary data in perl

I have the following predefined codes that represent an index in a binary bitmap:
0 = standard
1 = special
2 = regular
3 = late
4 = early
5 = on time
6 = generic
7 = rfu
An example value I would take as an input would be 213, which becomes 11010101 in binary. Index 0, 2, 4, 6, and 7 have their bit flipped indicating that this record is:
standard + regular + early + generic + rfu.
I am trying to figure out in perl how to take that binary data and build a string, like mentioned above with code + code + code, etc.
Any help would be greatly appreciated. Thanks.
Edit: My thoughts on how I might approach this are:
Convert decimal to binary
Find length of binary string
Using substr get the value (0 or 1) index by index
If index value = 1 then add relevant code to string
Is there a better way to go about this?
You can test bits on input from 0 to 7, and take only these that are set,
my $in = 213;
my #r = ("standard","special","regular","late","early","on time","generic","rfu");
print join " + ", #r[ grep { $in & (1 << $_) } 0 .. $#r ];
# or
# print join " + ", map { $in & (1<<$_) ? $r[$_] : () } 0 .. $#r;
output
standard + regular + early + generic + rfu