I can find sed solutions to delete all lines in a text file starting with a '1' as well as solutions to delete every other line of all the lines in the text file but I want to combine the two.. of all the lines starting with '1' delete every other one of them and keep the other lines that do not start with a 1.
So if I have a text file:
1, 1
1, 2
2, 3
3, 4
4, 5
2, 6
1, 7
3, 8
1, 9
4, 10
I want the output to be:
1, 1
2, 3
3, 4
4, 5
2, 6
1, 7
3, 8
4, 10
You could do this in awk:
awk -F, '!($1 == 1 && n++ % 2)' file
-F, means use comma as the field separator, so the two numbers on each line will be the variables $1 and $2.
awk will print the line if the last thing it evaluates is true. The ! negates the contents of the parentheses, so in order to print, the contents must be false.
If the first field isn't 1, short-circuiting takes place, as (false && anything) will always be false. This means that the second half after the && will not be evaluated.
If $1 == 1, then the second half is evaluated. As n is being used for the first time in a numeric context, it will assume the value 0. The modulo operation n % 2 will return 0 (false) for even numbers and 1 (true) for odd numbers. Using the increment n++ means that the result will alternate between true and false.
You may prefer the reverse logic, which would be:
awk -F, '$1 != 1 || ++n % 2' file
The || is also short-circuiting, so if the first value isn't 1 then the line gets printed. Otherwise, the second half is evaluated. This time, the increment goes before the n so that the first value of n is 1, making the expression evaluate to true.
Either way, the output is:
1, 1
2, 3
3, 4
4, 5
2, 6
1, 7
3, 8
4, 10
This might work for you (GNU sed):
sed '/^1/{x;/./{z;x;d};x;h}' file
Use the hold space to toggle the deletion of lines beginning with 1.
An alternative:
sed '/^1/{:a;n;//d;ba}' file
Here you go:
awk '$1=="1," && !(f=f?0:1) {next} 1' file
1, 1
2, 3
3, 4
4, 5
2, 6
1, 7
3, 8
4, 10
$1=="1," Test if first field is 1
f=f?0:1 Flop the f between 0 and 1 for every time $1=="1," is true
!(...) True if f is 0
Here's an awk-based solution without requiring any modulo math whatsoever :
[ngm]awk 'FS~NF||_*=--_' FS='^1'
|
1, 1
2, 3
3, 4
4, 5
2, 6
1, 7
3, 8
4, 10
This leverages the interesting property that x *= --x generates an alternating sequence of 1s and 0s that never converges and never diverges.
Related
I am a beginner level in this program. I try to improve this loop according to this condition. The details are as follows:
When CUTI(k) = CUTI(k)-4 then,
1)If the result shows this CUTI(k) value greater than 0, then print this CUTI(k) value.
2)If the result shows CUTI(k) value less than 0, then print this CUTI(k) value is added 12 with showing a word "*" after the number in display, e.g. 10*, 9*
I am not sure this loop is correct and enough to add this condition. Look forward to seeing your recoomendation. :)
set k /1*20/;
parameter
CUTI(k)/1 6, 2 2, 3 8, 4 5, 5 1, 6 3, 7 7, 8 8, 9 6, 10 8,11 1, 12 2, 13 4, 14 7,
15 5, 16 2, 17 8, 18 9, 19 2, 20 10/;
loop(k,
if(CUTI(k)-4 > 0,
CUTI(k) = CUTI(k)-4;
else
CUTI(k) = (CUTI(k)-4)+12 ;
)
);
display CUTI;
Your logic looks correct. However, instead of the loop/if/else you could simplify this to one assignment:
CUTI(k) = CUTI(k)-4+12$(CUTI(k)<=4);
However, modifying the display statement by adding a * to some elements is not possible. If you need to distinguish the cases in such a statement, you might assign the values to two different parameters and display them individually.
I'd like to find the contiguous sequences of equal elements (e.g. of length 2) in a list
my #s = <1 1 0 2 0 2 1 2 2 2 4 4 3 3>;
say grep {$^a eq $^b}, #s;
# ==> ((1 1) (2 2) (4 4) (3 3))
This code looks ok but when one more 2 is added after the sequence of 2 2 2 or when one 2 is removed from it, it says Too few positionals passed; expected 2 arguments but got 1 How to fix it? Please note that I'm trying to find them without using for loop, i.e. I'm trying to find them using a functional code as much as possible.
Optional: In the bold printed section:
<1 1 0 2 0 2 1 2 2 2 4 4 3 3>
multiple sequences of 2 2 are seen. How to print them the number of times they are seen? Like:
((1 1) (2 2) (2 2) (4 4) (3 3))
There are an even number of elements in your input:
say elems <1 1 0 2 0 2 1 2 2 2 4 4 3 3>; # 14
Your grep block consumes two elements each time:
{$^a eq $^b}
So if you add or remove an element you'll get the error you're getting when the block is run on the single element left over at the end.
There are many ways to solve your problem.
But you also asked about the option of allowing for overlapping so, for example, you get two (2 2) sub-lists when the sequence 2 2 2 is encountered. And, in a similar vein, you presumably want to see two matches, not zero, with input like:
<1 2 2 3 3 4>
So I'll focus on solutions that deal with those issues too.
Despite the narrowing of solution space to deal with the extra issues, there are still many ways to express solutions functionally.
One way that just appends a bit more code to the end of yours:
my #s = <1 1 0 2 0 2 1 2 2 2 4 4 3 3>;
say grep {$^a eq $^b}, #s .rotor( 2 => -1 ) .flat
The .rotor method converts a list into a list of sub-lists, each of the same length. For example, say <1 2 3 4> .rotor: 2 displays ((1 2) (3 4)). If the length argument is a pair, then the key is the length and the value is an offset for starting the next pair. If the offset is negative you get sub-list overlap. Thus say <1 2 3 4> .rotor: 2 => -1 displays ((1 2) (2 3) (3 4)).
The .flat method "flattens" its invocant. For example, say ((1,2),(2,3),(3,4)) .flat displays (1 2 2 3 3 4).
A perhaps more readable way to write the above solution would be to omit the flat and use .[0] and .[1] to index into the sub-lists returned by rotor:
say #s .rotor( 2 => -1 ) .grep: { .[0] eq .[1] }
See also Elizabeth Mattijsen's comment for another variation that generalizes for any sub-list size.
If you needed a more general coding pattern you might write something like:
say #s .pairs .map: { .value xx 2 if .key < #s - 1 and [eq] #s[.key,.key+1] }
The .pairs method on a list returns a list of pairs, each pair corresponding to each of the elements in its invocant list. The .key of each pair is the index of the element in the invocant list; the .value is the value of the element.
.value xx 2 could have been written .value, .value. (See xx.)
#s - 1 is the number of elements in #s minus 1.
The [eq] in [eq] list is a reduction.
If you need text pattern matching to decide what constitutes contiguous equal elements you might convert the input list into a string, match against that using one of the match adverbs that generate a list of matches, then map from the resulting list of matches to your desired result. To match with overlaps (eg 2 2 2 results in ((2 2) (2 2)) use :ov:
say #s .Str .match( / (.) ' ' $0 /, :ov ) .map: { .[0].Str xx 2 }
TIMTOWDI!
Here's an iterative approach using gather/take.
say gather for <1 1 0 2 0 2 1 2 2 2 4 4 3 3> {
state $last = '';
take ($last, $_) if $last == $_;
$last = $_;
};
# ((1 1) (2 2) (2 2) (4 4) (3 3))
I have been trying to find a way to move data labels either above or below points.
Starting at point 1, for every two consecutive points, I'd like to move the data labels above the point, and for the next 2, I'd like to move them below. I'm not sure how to change my for loop or if statement to accommodate this condition. So in the end, the data labels for points 1 and 2 would be positioned above, the data labels for points 3 and 4 would be positioned below, 5 and 6 above...etc
If anyone can help, I'd greatly appreciate it.
Dim FlowIndex As Long
With ActiveChart.SeriesCollection(1)
For FlowIndex = 1 To .Points.Count
With .Points(FlowIndex)
If .HasDataLabel Then
With .DataLabel
.Position = xlLabelPositionAbove
.Orientation = xlHorizontal
End With
End If
End With
Next
End With
This is a mathematical question of how to perform your loop. Think of the values 1,2,3,4,5,6,7,8. What mathematical rule would put 1,2,5,6 in the same group, and 3,4,7,8 in another group?
Possible answer: when divided by 4, 1 & 2 round down to 0. 3 & 4 round up to 1. 7&8 round up to 3. So, one group rounds up to an even number, and one group rounds down to an odd number. There are many ways to reflect this principle, but here is one:
IF FlowIndex MOD 4 = 1 OR FlowIndex MOD 4 = 2 then 'There is a remainder of 1 or 2 when divided by 4 - put label above
'Do stuff
Else 'There is a remainder of 3 or 0 when divided by 4 - put label below
'Do other stuff
End If
To clarify on my answer to the last question you had, MOD is a method of dividing which returns the remainder after doing whole number division. So 5 MOD 3 is 2, 9 MOD 3 is 0, etc.
Alternatively:
bLabelAbove = True
For i = 0 to 20 step 2
For j = 1 to 2
If bLabelAbove Then
Series.DataLabels(i + j).Position = xlLabelPositionAbove
Else
Series.DataLabels(i + j).Position = xlLabelPositionBelow
End If
Next
bLabelAbove = Not bLabelAbove
Next
Divide
1, 2, 3, 4, 5, 6, 7, 8
by 2, and round up, to get:
1, 1, 2, 2, 3, 3, 4, 4
Then you can test whether1, 1, 2, 2, 3, 3, 4, 4 is odd or even. If odd, then label above. If even, then label below.
I have the following code in VBA:
For n = 1 To 15
Cells(n, 8) = Application.Combin(2 * n, n)
next n
I want the n in the cells(n,8) to have an incerement 2, so the code skips a line after each entry.
Is it possible to have an other increment variable in this same loop that jumps 2 at once?
Thanks in advance!
EDIT: after reading the comment: I think what is needed is a counter to count, 1,2,3,4,5,6...15, and another one to count 1,3,5,7...15
For that, here is what is need to be done:
basically, you want the first iterator to be a normal counter,
and the second iterator to be odd numbers only.
So here is a simply input output table
input output
----- -----
1 1
2 3
3 5
4 7
5 9
6 11
From the above, we can deduce the formula needed to convert the input into the desired output: output = (input x 2) -1
And so, we can re-write our for loop to be like so:
For n=1 to 15
Cells(n,8) = Application.Combin(2*n-1,n)
Next
============= End of Edit =========================
Simply, use the keyword STEP in the for loop
For n = 1 To 15 STEP 2 'STEP can also be negative
'but you have to reverse the starting, and endin
'value of the iterator
The values for n will be: 1, 3, 5, 7, 9, 11 , 13, 15
Alternatively, use a local variable inside the for loop for that purpose (in-case you want the loop to execute 15 times)
For n=1 to 15
i = n + 1
Cells(i,8) = Application.Combine(2*n,n)
Next
Note This SO should not have the raku tag. It is too obsolete/misleading. The technical problem discussed in the question body no longer applies. The disagreement in the comments about naming/tags no longer applies. I'm leaving it for historical interest only, under the old tag only.
I am learning Perl 6, and had trouble understanding the Perl 6 one-liner below
My Perl 6 is rakudo-star: stable 2014.04 (bottled)
This works fine. The array/list is sorted
[njia#mb-125:~] : perl6 -e 'say [2443,5,33, 90, -9, 2, 764].sort'
-9 2 5 33 90 764 2443
But this does not sort the array/list, if [].sort works why #s.sort does not?
[njia#mb-125:~] : perl6 -e 'my #s = [2443,5,33, 90, -9, 2, 764]; #s.sort.say'
2443 5 33 90 -9 2 764
Change from [] to ()
[njia#mb-125:~] : perl6 -e 'my #s = (2443,5,33,90,-9,2,764); #s.sort.say'
-9 2 5 33 90 764 2443
NOTE the described behavior in this question has changed in the release version of perl6. See response by G. Cito below.
For those who may be confused by this answer, the question is about Perl 6, and none of this applies to Perl 5.
The statement
my #s = [2443, 5, 33, 90, -9, 2, 764]
creates an itemised array and assigns it to #s[0], so #s has only a single element and sorting it is pointless.
However you can say
#s[0].sort.say
which has the effect you expected
I'm going to go out on a limb and refer to some of CPAN's Perl6 documentation where this could be viewed as a list vs. array thing - i.e. a sequence of values versus a sequence of itemized values (see doc.perl6.org).
Certainly perl6 is different enough that it warrants its own tag but it is still perl so it's not surprising that () creates a list and [] creates an anonymous array.
> say [2443, 5, 33, 90, -9, 2, 764].WHAT
(Array)
> say (2443, 5, 33, 90, -9, 2, 764).WHAT
(List)
Since this question was first asked and answered the behavior has changed:
> my #s = [2443, 5, 33, 90, -9, 2, 764]
> #s.sort.say
(-9 2 5 33 90 764 2443)
Note that the output when sorted is a List but otherwise #s is an Array:
> #s.sort.WHAT.say
(List)
> #s.WHAT.say
(Array)