Retrieve numbers before/after sign change in [Double] - arrays

I have a [Double] with values like [60, 21, -18, -57, -95, -67, -29, 8, 45, 82].
Is there a quick way with Swift to retrieve the numbers before the next element is a number with the opposite sign?
With the example data set, the returned values should be: -18, 21 and -29, 8 or their equivalent indexes in the array.
I know I can use .filter() to compare values and sort them to retrieve the max and min of the entire set, but retrieving multiple ranges dependent on sign changes (can be more than two) is a bit more confusing for me.

I would do it like this:
let highestNegative = values.filter { $0 < 0 }.max()
let lowestPositive = values.filter { $0 > 0 }.min()
Both will be optional values. 0 will be ignored, but you can include it by modifying one of the operators.

([60, 21, -18, -57, -95, -67, -29, 8, 45, 82] as [Double])
.consecutivePairs
.filter { $0.sign != $1.sign }
public extension Sequence {
/// Each element of the sequence, paired with the element after.
var consecutivePairs: Zip2Sequence<Self, DropFirstSequence<Self>> {
zip(self, dropFirst())
}
}

You just need to check if the last value is greater than zero and the current is less than zero, if true add the range to the results. Just make sure you add the lower value as the lower bound and the upper value as the upper bound:
let values: [Double] = [60, 21, -18, -57, -95, -67, -29, 8, 45, 82]
var last = values.last ?? 0
let ranges = values.dropFirst().compactMap { value -> ClosedRange<Double>? in
defer { last = value }
return last > 0 && value < 0 || last < 0 && value > 0 ? min(last,value)...max(last,value) : nil
}
print(ranges) // [ClosedRange(-18.0...21.0), ClosedRange(-29.0...8.0)]

Related

How to find out if an arithmetic sequence exists in an array

If there is an array that contains random integers in ascending order, how can I tell if this array contains a arithmetic sequence (length>3) with the common differece x?
Example:
Input: Array=[1,2,4,5,8,10,17,19,20,23,30,36,40,50]
x=10
Output: True
Explanation of the Example: the array contains [10,20,30,40,50], which is a arithmetic sequence (length=5) with the common differece 10.
Thanks!
I apologize that I have not try any code to solve this since I have no clue yet.
After reading the answers, I tried it in python.
Here are my codes:
df = [1,10,11,20,21,30,40]
i=0
common_differene=10
df_len=len(df)
for position_1 in range(df_len):
for position_2 in range(df_len):
if df[position_1] + common_differene == df[position_2]:
position_1=position_2
i=i+1
print(i)
However, it returns 9 instead of 4.
Is there anyway to prevent the repetitive counting in one sequence [10,20,30,40] and also prevent accumulating i from other sequences [1,11,21]?
You can solve your problem by using 2 loops, one to run through every element and the other one to check if the element is currentElement+x, if you find one that does, you can continue form there.
With the added rule of the sequence being more than 2 elements long, I have recreated your problem in FREE BASIC:
DIM array(13) As Integer = {1, 2, 4, 5, 8, 10, 17, 19, 20, 23, 30, 36, 40, 50}
DIM x as Integer = 10
DIM arithmeticArrayMinLength as Integer = 3
DIM index as Integer = 0
FOR position As Integer = LBound(array) To UBound(array)
FOR position2 As Integer = LBound(array) To UBound(array)
IF (array(position) + x = array(position2)) THEN
position = position2
index = index + 1
END IF
NEXT
NEXT
IF (index <= arithmeticArrayMinLength) THEN
PRINT false
ELSE
PRINT true
END IF
Hope it helps
Edit:
After reviewing your edit, I have come up with a solution in Python that returns all arithmetic sequences, keeping the order of the list:
def arithmeticSequence(A,n):
SubSequence=[]
ArithmeticSequences=[]
#Create array of pairs from array A
for index,item in enumerate(A[:-1]):
for index2,item2 in enumerate(A[index+1:]):
SubSequence.append([item,item2])
#finding arithmetic sequences
for index,pair in enumerate(SubSequence):
if (pair[1] - pair[0] == n):
found = [pair[0],pair[1]]
for index2,pair2 in enumerate(SubSequence[index+1:]):
if (pair2[0]==found[-1] and pair2[1]-pair2[0]==n):
found.append(pair2[1])
if (len(found)>2): ArithmeticSequences.append(found)
return ArithmeticSequences
df = [1,10,11,20,21,30,40]
common_differene=10
arseq=arithmeticSequence(df,common_differene)
print(arseq)
Output: [[1, 11, 21], [10, 20, 30, 40], [20, 30, 40]]
This is how you can get all the arithmetic sequences out of df for you to do whatever you want with them.
Now, if you want to remove the sub-sequences of already existing arithmetic sequences, you can try running it through:
def distinct(A):
DistinctArithmeticSequences = A
for index,item in enumerate(A):
for index2,item2 in enumerate([x for x in A if x != item]):
if (set(item2) <= set(item)):
DistinctArithmeticSequences.remove(item2)
return DistinctArithmeticSequences
darseq=distinct(arseq)
print(darseq)
Output: [[1, 11, 21], [10, 20, 30, 40]]
Note: Not gonna lie, this was fun figuring out!
Try from 1: check the presence of 11, 21, 31... (you can stop immediately)
Try from 2: check the presence of 12, 22, 32... (you can stop immediately)
Try from 4: check the presence of 14, 24, 34... (you can stop immediately)
...
Try from 10: check the presence of 20, 30, 40... (bingo !)
You can use linear searches, but for a large array, a hash map will be better. If you can stop as soon as you have found a sequence of length > 3, this procedure takes linear time.
Scan the list increasingly and for every element v, check if the element v + 10 is present and draw a link between them. This search can be done in linear time as a modified merge operation.
E.g. from 1, search 11; you can stop at 17; from 2, search 12; you can stop at 17; ... ; from 8, search 18; you can stop at 19...
Now you have a graph, the connected components of which form arithmetic sequences. You can traverse the array in search of a long sequence (or a longest), also in linear time.
In the given example, the only links are 10->-20->-30->-40->-50.

Finding surrounding elements of value in Array

I have an array of CGFloats. I also have an arbitrary value a that can be any CGFloat. My question is, how do I efficiently find which two indices that a is between. As a side note, a will never be below or greater than the minimum or maximum of the Array, so there is no need to worry about that.
For a simplifed example, I may have:
let array: [CGFloat] = [4, 7, 10, 22, 23, 25, 67]
// a can be any random number, this initialization is for the example
let a = 14
// some algorithm that calculates indexes
// code returns index 2 and 3 (or it returns items 10, 22)
I have developed one method involving for loops, however, the larger the list is the more inefficient the code is. Is there any intelligent, and more efficient code out there?
Thanks for all the help :)
What you are looking for is called mid binary search. There is many examples out there of this kind of approach Example #2. Note that if you pass a value lower than the first value it will return the start index and a value higher than the last value it will return the last index.
extension Collection where Element: Comparable, Index == Int {
func binarySearch(_ element: Element) -> Index {
var low = 0
var high = count - 1
while low < high {
let mid = low + ((high - low + 1) / 2)
let current = self[mid]
if current == element {
return mid
} else if current < element {
low = mid
} else {
high = mid - 1
}
}
return low
}
}
let array: [CGFloat] = [4, 7, 10, 22, 23, 25, 67]
let a = 14
let indexA = array.binarySearch(CGFloat(a)) // 2
let indexB = indexA + 1 // 3
if Your array is always sorted use:
let array: [CGFloat] = [4, 7, 10, 22, 23, 25, 67]
let a: CGFloat = 14
if let maxIndex = array.firstIndex(where: { $0 > a }), maxIndex > 0 {
print("a between \(maxIndex - 1) and \(maxIndex) indexes")
}

Reduce array to tuple of first and last element?

I have an array that I would like to first sort, then return the first and last element of the sorted array. I thought I can use reduce, but what if I don't have an initial value?
Here is the array I'm trying to work with:
let myNumbers = [4, 9, 6, 2, 3]
How can map this to the first and last of the sorted array to this?:
(2, 9)
Method 1: min()/max()
This is the easiest way:
let input = [4, 9, 6, 2, 3]
let output = (input.min(), input.max())
print(output) //(Optional(2), Optional(9))
If you're certain that the array isn't empty, you can safely force unwrap the optionals:
let input = [4, 9, 6, 2, 3]
let output = (input.min()!, input.max()!) // (2, 9)
This is approach does 2 iterations over the array. It's O(N). Unless a sorted list is required elsewhere, sorting then taking the first/last would be worse, as it would be O(N * log_2(N)).
Method 2: reduce()
If you insist on using reduce, you can do it like this:
let input = [4, 9, 6, 2, 3]
let output = input.reduce((min: Int.max, max: Int.min)){
(min($0.min, $1), max($0.max , $1))
} //(2, 9)
Each reduce iteration sets the accumulator to the new min (the smaller of the old min and current element), and the new max (the larger of the old max and the current).
The initial values of the accumulator are set such that:
Any element in the array compares as smaller than the accumulator's min
Any element in the array compares as larger than the accumulator's max
You don't need an initialValue for reduce, it's optional.
var foo = [1, 40, 20, -20, 50];
var reducer = function(prev, curr, i, arr){return [prev[0] <= curr ? prev[0] : curr, prev[1] >= curr ? prev[1] : curr]};
var baz = foo.reduce(reducer); // [-20, 50]
Or maybe like this:
var foo = [1, 40, 20, -20, 50];
var reducer = function(prev, curr, i, arr){return {min: prev.min <= curr ? prev.min : curr, max: prev.max >= curr ? prev.max : curr}};
var baz = foo.reduce(reducer); // {min: -20, max: 50}
Edit: Just noticed this is for swift and not javascript, whoops lol. I must have been surfing the wrong SO category. I think the principle would be the same in swift except you probably do need to provide some kind of initial value.

How can I create a method from Array to generate a tuple - Swift

let array = [52, 5, 13, 126, 17]
I think what i have to do is I need to use filter on a collection
then need to use filter 3 times in this case with 3 different conditions
Filter will return an array so I have to use its count to store it in the tuple.
You can get The number of values in the original array matching a certain condition, with filter and count like this:
(Codes below written and tested in Swift 3.)
//The number of values in the original array in the range 0 ..< 50.
let theNumberValuesInRange1 = intArray.filter{0..<50 ~= $0}.count
You can do the same things 3 times and write something like this:
func getTheNumbersInRanges(_ intArray: [Int]) -> (Int, Int, Int) {
let theNumberValuesInRange1 = intArray.filter{0..<50 ~= $0}.count
let theNumberValuesInRange2 = intArray.filter{50..<100 ~= $0}.count
let theNumberValuesInRange3 = intArray.filter{100 <= $0}.count
return (theNumberValuesInRange1, theNumberValuesInRange2, theNumberValuesInRange3)
}
print(getTheNumbersInRanges([52, 5, 13, 126, 17])) //->(3, 1, 1)
But filter generates intermediate Array, so, it's not efficient especially for big Arrays.
You can use reduce, with which no intermediate Arrays are generated.
let theNumberRange1Way2 = intArray.reduce(0) {count, value in 0..<50 ~= value ? count + 1 : count}
Applying this way to tuple, you can write something like this:
func getTheNumbersInRangesWay2(_ intArray: [Int]) -> (Int, Int, Int) {
return intArray.reduce((0, 0, 0)) {countTuple, value in
switch value {
case 0 ..< 50:
return (countTuple.0 + 1, countTuple.1, countTuple.2)
case 50 ..< 100:
return (countTuple.0, countTuple.1 + 1, countTuple.2)
case let v where 100 <= v:
return (countTuple.0, countTuple.1, countTuple.2 + 1)
default:
return countTuple
}
}
}
print(getTheNumbersInRangesWay2([52, 5, 13, 126, 17])) //->(3, 1, 1)

Element by Element Comparison of Multiple Arrays in MATLAB

I have a multiple input arrays and I want to generate one output array where the value is 0 if all elements in a column are the same and the value is 1 if all elements in a column are different.
For example, if there are three arrays :
A = [28, 28, 43, 43]
B = [28, 43, 43, 28]
C = [28, 28, 43, 43]
Output = [0, 1, 0, 1]
The arrays can be of any size and any number, but the arrays are also the same size.
A none loopy way is to use diff and any to advantage:
A = [28, 28, 43,43];
B = [28, 43, 43,28];
C = [28, 28, 43,43];
D = any(diff([A;B;C])) %Combine all three (or all N) vectors into a matrix. Using the Diff to find the difference between each element from row to row. If any of them is non-zero, then return 1, else return 0.
D = 0 1 0 1
There are several easy ways to do it.
Let's start by putting the relevant vectors in a matrix:
M = [A; B; C];
Now we can do things like:
idx = min(M)==max(M);
or
idx = ~var(M);
No one seems to have addressed that you have a variable amount of arrays. In your case, you have three in your example but you said you could have a variable amount. I'd also like to take a stab at this using broadcasting.
You can create a function that will take a variable number of arrays, and the output will give you an array of an equal number of columns shared among all arrays that conform to the output you're speaking of.
First create a larger matrix that concatenates all of the arrays together, then use bsxfun to take advantage of broadcasting the first row and ensuring that you find columns that are all equal. You can use all to complete this step:
function out = array_compare(varargin)
matrix = vertcat(varargin{:});
out = ~all(bsxfun(#eq, matrix(1,:), matrix), 1);
end
This will take the first row of the stacked matrix and see if this row is the same among all of the rows in the stacked matrix for every column and returns a corresponding vector where 0 denotes each column being all equal and 1 otherwise.
Save this function in MATLAB and call it array_compare.m, then you can call it in MATLAB like so:
A = [28, 28, 43, 43];
B = [28, 43, 43, 28];
C = [28, 28, 43, 43];
Output = array_compare(A, B, C);
We get in MATLAB:
>> Output
Output =
0 1 0 1
Not fancy but will do the trick
Output=nan(length(A),1); %preallocation and check if an index isn't reached
for i=1:length(A)
Output(i)= ~isequal(A(i),B(i),C(i));
end
If someone has an answer without the loop take that, but i feel like performance is not an issue here.

Resources