How to delete the elements of array from index 4 to 10. I am doing like this
#!usr/bin/perl
#array=1..10;
#array[0..3]=#array[5..8];
#array[4..10]=();
$str=(join ",",#array);
print "$str\n";
and getting output as
6,7,8,9,,,,,,,
How to remove extra ","?
You're doing a bit more than trying to delete elements of the array in that code...
Anyways, you want splice to remove a contiguous range of array elements:
splice #array, 4, 6;
I'm assuming this is some kind of homework question, because if it's not you're doing some REALLY strange things with this code.
But let's take the problem at hand - the easiest way to filter an array is with grep
my #no_empty = grep { defined } #array;
print join ( ",", #no_empty )
In this we use grep to filter out any undef array elements. The element is returned if the expression in the braces evaluates as true - $_ is set to the current element, and used in any tests that implicitly work on $_. defined does, as does regular expression matches.
However if you genuinely just want to truncate the array to an arbitrary number, you can just do:
#array = #array[0..4]
You start with this array:
my #array = 1..10;
Now you have an array of 10 elements.
1 2 3 4 5 6 7 8 9 10
In your next step, you perform an array slice then assign that to another array slice:
#array[0..3] = #array[5..8];
This leaves alone the elements from 5 to 8, but does a list assignment of their values to the elements from 0 to 3, so you end up with some duplicated elements:
6 7 8 9 5 6 7 8 9 10
Now you want to get rid of everything after index 4. You try that with another list assignment:
#array[4..10] = ();
However, that just assigns the list on the right to the list on the right. As with any list assignment, the left hand elements get their corresponding elements from the right hand list. If the right hand list doesn't have enough elements, Perl uses undef for the rest of the right hand elements. That's why you still have 10 elements in #array:
6 7 8 9 undef undef undef undef undef undef undef
If you want 4 to be the last index, you can assign to the last index of the array. Whatever number you assign becomes the last index, either shortening or extending the array. Since you wanted to get rid of elements 4 and beyond, you can set the last index to 3 (the index right before the one you want to remove):
$#array = 3;
Now you've truncated the array to four elements:
6 7 8 9
You can also do this with splice, which replaces part of an array:
splice #array, $start_index, $length, #replacement;
You might use the starting index to figure out the length since you want to go all the way to the end. Then, replace that portion of the array with the empty list, which effectively makes the array shorter:
my $start = 4;
splice #array, $start, #array - $start, ();
Leaving off the replacement list is the same as the empty list:
splice #array, $start, #array - $start;
This is much more handy when you want to remove parts in the middle. This removes three elements starting at index 4 (so there will be stuff left over at the end):
splice #array, 4, 3;
Now your array has elements that were at the beginning and end of your array:
6,7,8,9,8,9,10
Without shortening the array
There's another sort of problem. You don't want to change the array, but you don't want to deal with empty fields. You can use a grep to select only the defined elements:
say join ',', grep { defined } #array;
If you have undefined elements in the middle of the array, this might be a problem if you expect columns to line up properly. Removing a column in the middle shifts the other columns. You may not care about that though.
Similarly, you might turn the undefined values into something that makes sense for the problem. A map can inspect the value and decide to pass it through or transform it. In this example, undef values turn into 0:
say join ',', map { defined ? $_ : 0 } #array;
6,7,8,9,0,0,0,0,0,0,0
Or "NULL":
say join ',', map { defined ? $_ : 'NULL' } #array;
6,7,8,9,NULL,NULL,NULL,NULL,NULL,NULL,NULL
Or even just undef as a string:
say join ',', map { defined ? $_ : 'undef' } #array;
6,7,8,9,undef,undef,undef,undef,undef,undef,undef
Sometimes those are handy to see what's going on.
And map can act like grep to filter an array. Use the empty list to pass on no elements from the map:
say join ',', map { defined ? $_ : () } #array;
Related
I have a powershell array with about 10 items. I'm trying to take the 1st element (0) of the array and move it to the last item. There will still be (only) 10 items in the array at the end.
If you don't mind creating a new array, here's a concise solution:
$a = 1..10 # sample array: 1, 2, ..., 10
$a = $a[1..($a.count-1)] + $a[0]
minmaxavg eventually came up with an even more concise alternative in their answer:
$a[1..($a.count-1) + 0]
Performance-wise the two solutions are virtually identical.
Also, their [array]::Copy() solution is a more efficient, in-place alternative - at the expense of being more complex to write; it could be simplified somewhat to:
$first = $a[0]; [array]::Copy($a, 1, $a, 0, $a.Count - 1); $a[-1] = $first
$a now contains:
2
3
4
5
6
7
8
9
10
1
$a[1..($a.count-1)] returns a new array that contains all elements of $a starting with the element at index 1, i.e., starting with the 2nd element.
1..($a.count-1) is a range expression which itself creates an array - the array of indices to extract from the input array.
As an aside:
PowerShell supports index -1 to refer to an array's last element, -2 to refer to the second-to-last, and so on.
However, using negative indices in a range may not do what you expect; e.g., 1..-1 does not extract all elements from the 2nd to the second-to-last one (GitHub issue #7940 proposes new syntax similar to C#'s ranges to support this use case); instead, it extracts elements with indices 1, 0, and -1 (the last), because these are the elements of the array (of indices) that the range operator (..) generates.
+ $a[0] "appends" the first element to that new array; technically, this creates another array that is the concatenation of the elements of the LHS and the RHS.
In summary, 3 arrays were created in the process: 2 auxiliary, transient ones behind the scenes, plus the result array assigned back to $a.
Therefore, while using the range operator (..) to extract array slices and using + to concatenate arrays is very convenient, it is not efficient, though with small arrays that usually doesn't matter - beware of "appending" to an array in a loop with +=, however, as you'll be recreating the array in every iteration.
If you want to rotate the array in-place, this is more efficient than manually iterating and shifting elements in Powershell (.NET Array.Copy is analogous to Java System.arraycopy):
New-Variable -Name first -Value $arr[0] -Scope Private
[array]::Copy($arr, 1, $arr, 0, $arr.Length - 1)
$arr[$arr.Length - 1] = $Private:first
If copying is desirable (10 elements should be ok):
$arr = $arr[1 .. ($arr.Length - 1) + 0]
Powershell allows combination of two or more ranges through the + operator. Especially if you intend the array to be immutable, this should be the most optimal solution.
I am using below commands to find the size of array
$size_x = #x;
or
$size_x = $#x+1 ;
Both work well when I use in simple statement. But when I use them in the loop, the size becomes one number bigger. Why it is happening in that way. Below is example:
for ($i=1;$i<=10;$i++){
if (1**2+2**2>=1){
#x[$i] =2+3;
$size_x = #x;
}
print "my size is $size_x\n";
}
here is results:
my size is 2
my size is 3
my size is 4
my size is 5
my size is 6
my size is 7
my size is 8
my size is 9
my size is 10
my size is 11
The answer should be from 1 to 10 instead of 2 to 11, i think. What is a better way to get size correctly? Thanks.
After reading your code, I honestly can't figure out what you're trying to do here, but if you're trying to create an array with 11 elements and assign all of them to 5 except for the first one, then you've done an excellent job. Perhaps it would help to see a visualization of the array you've created:
[undef, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
If that's really what you were hoping for, there are faster/simpler/better ways to do the same thing, but I have to believe you were trying to accomplish something else. At a very basic level, arrays in Perl are 0-indexed:
Normal arrays are ordered lists of scalars indexed by number, starting
with 0.
Having said that, you very rarely see the C-style for loop in Perl code, and that's because it's very rarely necessary. More often, you would see something like this:
for (0 .. 9) {
# do something...
}
That code uses a foreach-style loop and the range operator (..) to generate a sequence (technically, a list of values from left to right, counting up by one). Enough about loops. Let's talk about your strange logic.
12 + 22 will always be 5, which will always be greater than 1. The same goes for 2 + 3. There's nothing dynamic about this code, which is why I have to think that you meant to use the loop iterator in your calculations somehow. Otherwise, you could easily fill a 10-element array with a bunch of 5's in one line:
my #x = (5) x 10;
Now, one last point that applies to this code and all Perl code you write from this point forward. Every single file must include the following two lines at the top:
use strict;
use warnings;
I believe that Perl arrays are zero-based, meaning that the first element is stored and accessed at index 0. Here is your for loop:
for ($i=1; $i<=10; $i++) {
if (1**2+2**2>=1) {
#x[$i] =2+3;
$size_x = #x;
}
print "my size is $size_x\n";
}
When you make the first assignment #x[1] = 2 + 3, the array is considered to already have an (empty) element in position zero, so the size is returned as being 2.
The easiest way to avoid this problem from happening would be to refactor your for loop to start at index 0:
for ($i=0; $i<10; $i++) {
...
}
When you start the index from 1 the size automatically increases by 1. Try starting the index from 0 or
#x[$i-1] =2+3;
Just as a side note:
Array elements in Perl are denoted with a starting $, not an #.
So #x is the whole array, while $x[$i] is the $i'th element of the array #x.
use warnings; should warn about this.
(I know this is more a comment than an answer, but as a SO newbie I'm not yet allowed to comment. Sorry.)
I have 7 arrays that are defined in the following manner:
my #array1 = ();
..
my #array7 = ();
and then:
$array1[0] = "text goes here";
..
$array7[0] = "text goes here";
There are about 25 elements) in each of the seven arrays, that is, $array1[0] to $array1[24]. I need to frequently change the content of these arrays in various scripts. And sometimes, because the order of the arrays is essential, I need to rearrange the order of the array index or delete the elements at a position. This is a real pain in the ass, as I need to change the index of all subsequent arrays. Just to make it clear, if I delete array1[12], then I need to change $array1[13] to $array1[12] and that for all 7 arrays and for all subsequent index positions (or move the content of array1[13] to array1[12], etc.)
So my question is, is it possible to impute the index of the arrays so that I could switch around the arrays position without having to correct each array index afterwards? Something like this:
$array1[$_] = "text 1 goes here";
..
$array7[$_] = "other text 1 goes here";
and then:
$array1[$_] = "text 2 goes here";
..
$array7[$_] = "other text 2 goes here";
where $_ would be replaced by 1 for the first index of each of the 7 arrays and by 2 for the next element of each of the 7 arrays... (up to 24 elements).
Is there a solution to this problem other than using an hash and Tie::Hash?
EDIT
ok, let me clarify. I am looking for a script maintenance solution, no for a solution about the output of the script. I need to change the script myself (by hand) frequently and I do not want to change the numbers indexing all 24 positions of all 7 arrays by hand whenever I change something in these arrays. So my question was, is there a way to have the script impute the numbers indexing all positions of all arrays?
Using push as mvp was suggesting would be a proper solution. Are there any other solutions that could involve loops or something rather than using push 7X24 times?
Not quite sure what your question is. You are probably complicating things quite a bit. Are you looking for splice?
my #a = (1 .. 4);
splice #a, 2, 1; # remove 1 element, starting at index 2
# #a is now 1, 2, 4
splice can also insert elements and remove more than one element:
splice ARRAY or EXPR, OFFSET, LENGTH, LIST
You can use this approach:
my #array1;
push #array1, "text 1 goes here";
push #array1, "other text 1 goes here";
# ...
# or you can use loop as well:
for my $i (1..10) {
push #array1, "text 1 instance $i goes here";
}
# do it for another one:
my #array2;
push #array2, "text 2 goes here";
push #array2, "other text 2 goes here";
# ...
You can even do this (little bit nasty because of dynamic variables):
for my $i (1..7) {
for my $j (1..24) {
push #{"array$i"}, "text $i, instance $j goes here";
}
}
I am writing a program and trying to break up data, which is stored in an array, in order to make it run faster.
I am attempting to go about it this way:
data_to_analyze=(1 2 3 4 5 6 7 8 9 10)
#original array size
dataSize=(${#data_to_analyze[#]})
#half of that size
let samSmall="$dataSize/2"
#the other half
let samSmall2=("$dataSize - $samSmall -1")
#the first half
smallArray=("${data_to_analyze[#]:0:$samSmall}")
#the rest
smallArray2=("${data_to_analyze[#]:$samSmall:$samSmall2}")
#an array of names(which correspond to arrays)
combArray=(smallArray smallArray2)
sizeComb=(${#combArray[#]})
#for the length of the new array
for ((i=0; i<= $sizeComb ; i++)); do
#through first set of data and then loop back around for the second arrays data?
for sample_name in ${combArray[i]}; do
command
wait
command
wait
done
What I imagine this does is gives only the first array of data to the for loop at first. When the first array is done it should go through again with the second array set.
That leaves me with two questions. Is combArray really passing the two smaller arrays? And is there a better way?
You can make a string that looks like an array reference then use it to indirectly access the elements of the referenced array. It even works for elements that contain spaces!
combArray=(smallArray smallArray2)
for array in "${combArray[#]}"
do
indirect=$array[#] # make a string that sort of looks like an array reference
for element in "${!indirect}"
do
echo "Element: $element"
done
done
#!/bin/bash
data_to_analyze=(1 2 3 4 5 6 7 8 9 10)
dataSize=${#data_to_analyze[#]}
((samSmall=dataSize/2,samSmall2=dataSize-samSmall))
smallArray=("${data_to_analyze[#]:0:$samSmall}")
smallArray2=("${data_to_analyze[#]:$samSmall:$samSmall2}")
combArray=(smallArray smallArray2)
sizeComb=${#combArray[#]}
for ((i=0;i<$sizeComb;i++));do
eval 'a=("${'${combArray[i]}'[#]}")'
for sample_name in "${a[#]}";do
...
done
done
EDIT: removed the double quotes near ${combArray[i]} and replaced <= by < in for
Does anyone knows why $#a one less than the actually number of elements in an array?
$ perl -we '#a=(1,2);print $#a'
1
That's the index of the last item and since arrays start at zero (unless you're messing around with things that are better left alone), $#a is one less than the length of the array.
I would imagine that is because it is the index of the last element in the array. Since the array indexing begins at 0, you need to add one to get the total number of elements in an array.
NB: You can also do this to find the count of an array:
#arr = ("one", "two");
$count = #arr;
print $count;
Array #a = ("a","b","c");
Value of $#a = index of last element(=2).
$a[0] = "a";
$a[1] = "b";
$a[2] = "c";
if you want to get number of elements in the array, you can assign the array to a scalar
like
$arrayLength = #a; #(=3)
Hope this helps you
$#array is used to find the index of the last element in the array.
In the example above it is position 1 - as indexes in arrays start at 0 in perl.
Everyone is telling you what it returns (which you already know), but you asked why it returns what it does.
Unfortunately, it's not really possible to answer why it returns what it does. Maybe because it makes the following easier?
for (0..$#a) {
...
}