Suppose I have three cells, one containing the distance, one the speed and the other the time. Now I want to be able to fill in two cells (e.g. distance and speed) and have the time calculated in the third cell. But when I fill in the speed and the time, the distance should be calculated.
How can this be achieved in google spreadsheets? I suppose there should be some kind of check to not create an infinite loop. I tried to search for cell synchronization or cell dependency, but I think this is not the right term for this problem.
You can do this with a nested if statement:
=if(And(isnumber(A2),isnumber(C2)),ROUND(A2/C2),if(And(isnumber(A2),isnumber(B2)),round(A2/B2),if(and(isnumber(B2),isnumber(C2)),B2*TIMEVALUE(C2)*24,)))
Here is a screenshot for all three scenarios:
You may use OnEdit event:
user enters some value (speed, row, distance)
script checks for other values,
calculates the result
paste the result into cells
The whole text of script is here:
function onEdit(t) {
var workSheetName = 'Sheet1'; // change sheet name to yours
var distanceRangeAddress = 'A2:A'; // change if needed
var speedRangeAddress = 'B2:B'; // change if needed
var timeRangeAddress = 'C2:C'; // change if needed
var ss = SpreadsheetApp.getActiveSheet();
var sn = ss.getName();
var distance, speed, time;
if (sn == workSheetName) {
var tr = t.range;
if (t.oldValue != tr.getValue() && tr.getNumColumns() == 1 && tr.getNumRows() == 1) {
// distance
var myr = ss.getRange(distanceRangeAddress);
if (RangeIntersect(tr, myr)) {
distance = tr.getValue();
speed = tr.offset(0, 1).getValue();
time = tr.offset(0, 2).getValue();
if (time > 0) {
speed = distance / time;
tr.offset(0, 1).setValue(time);
return;
}
if (speed > 0) {
time = distance / speed;
tr.offset(0, 2).setValue(time);
return;
}
}
// speed
myr = ss.getRange(speedRangeAddress);
if (RangeIntersect(tr, myr)) {
speed = tr.getValue();
distance = tr.offset(0, -1).getValue();
time = tr.offset(0, 1).getValue();
if (time > 0) {
distance = speed * time;
tr.offset(0, -1).setValue(distance);
return;
}
if (distance > 0) {
time = distance / speed;
tr.offset(0, 1).setValue(time);
return;
}
}
// time
myr = ss.getRange(timeRangeAddress);
if (RangeIntersect(tr, myr)) {
time = tr.getValue();
distance = tr.offset(0, -2).getValue();
speed = tr.offset(0, -1).getValue();
if (speed > 0) {
distance = speed * time;
tr.offset(0, -2).setValue(distance);
return;
}
if (distance > 0) {
speed = distance / time;
tr.offset(0, -1).setValue(speed);
return;
}
}
}
}
}
function RangeIntersect(R1, R2) {
var LR1 = R1.getLastRow();
var Ro2 = R2.getRow();
if (LR1 < Ro2) return false;
var LR2 = R2.getLastRow();
var Ro1 = R1.getRow();
if (LR2 < Ro1) return false;
var LC1 = R1.getLastColumn();
var C2 = R2.getColumn();
if (LC1 < C2) return false;
var LC2 = R2.getLastColumn();
var C1 = R1.getColumn();
if (LC2 < C1) return false;
return true;
}
You may want to change parametres in the beginning of the script:
var workSheetName = 'Sheet1'; // change sheet name to yours
var distanceRangeAddress = 'A2:A'; // change if needed
var speedRangeAddress = 'B2:B'; // change if needed
var timeRangeAddress = 'C2:C'; // change if needed
Here's example file.
The script could be improved and adopted to your needs. Please, look for more information here.
Related
I'm attempting to get a user input number and find the sum of all the digits. I'm having issues with larger numbers, however, as they won't register under an Int64. Any idea as to what structures I could use to store the value? (I tried UInt64 and that didn't work very well with negatives, however, I'd prefer something larger than UInt64, anyways. I'm having a hard time implementing a UInt128 from Is there a number type with bigger capacity than u_long/UInt64 in Swift?)
import Foundation
func getInteger() -> Int64 {
var value:Int64 = 0
while true {
//we aren't doing anything with input, so we make it a constant
let input = readLine()
//ensure its not nil
if let unwrappedInput = input {
if let unwrappedInt = Int64(unwrappedInput) {
value = unwrappedInt
break
}
}
else { print("You entered a nil. Try again:") }
}
return value
}
print("Please enter an integer")
// Gets user input
var input = getInteger()
var arr = [Int] ()
var sum = 0
var negative = false
// If input is less than 0, makes it positive
if input < 0 {
input = (input * -1)
negative = true
}
if (input < 10) && (input >= 1) && (negative == true) {
var remain = (-1)*(input%10)
arr.append(Int(remain))
input = (input/10)
}
else {
var remain = (input%10)
arr.append(Int(remain))
input = (input/10)
}
}
// Adds numbers in array to find sum of digits
var i:Int = 0
var size:Int = (arr.count - 1)
while i<=size {
sum = sum + arr[i]
i = (i+1)
}
// Prints sum
print("\(sum)")
You can use a string to perform the operation you describe. Loop through each character and convert it to an integer and add to the sum. Be careful to handle errors.
I am trying to animate a line based on the given coordinates array comprising of latitude and longitude and I want to call my function just once and my coordinates name is: replayData.
map.on('postcompose', function (event) {
var vectorContext = event.vectorContext;
var frameState = event.frameState;
vectorContext.setFillStrokeStyle(null, animatedLineStroke);
var features = lineVectorSource.getFeatures();
for (var k = 0; k < features.length; k++) {
var feature = features[k];
if (!feature.get('finished')) {
var coords = feature.getGeometry().getCoordinates();
var elapsedTime = frameState.time - feature.get('start');
var elapsedPoints = elapsedTime * pointsPerMs;
if (elapsedPoints >= coords.length) {
feature.set('finished', true);
}
var maxIndex = Math.min(elapsedPoints, coords.length);
var currentLine = new ol.geom.LineString(coords.slice(0, maxIndex));
if (feature.get('iValue') == undefined || feature.get('iValue') < k) {
// drawMovingCarMarker(coords, k);
feature.set('iValue', k)
}
vectorContext.drawLineStringGeometry(currentLine, feature);
}
}
frameState.animate = true;
});
What this function is doing is first collecting all the values from for loop and then starting the animation. And because of this if I've 5 points the line between first two points will be drawn 5 times then 4,3, and so on.
Any help would be entertained. Thanks in advance.
The question is related to calculating an increase in currency.
Loop over this n times, and let's say you start with $50k and your multiplier is 2. Something like b * 2 + a
This is the correct result:
$50,000.00
$100,000.00
$250,000.00
$600,000.00
$1,450,000.00
$3,500,000.00
$8,450,000.00
$20,400,000.00
$49,250,000.00
So just to be clear, the question is about efficiency in swift, not simply how to calculate this. Are there any handy data structures that would make this faster? Basically I was just looping through how many years (n) adding 2 (200%) and incrementing a couple temp variables to keep track of the current and previous values. It feels like there has got to be a much better way of handling this.
$50k base
$50k * 2 + 0 (previous value) = $100k
$100k * 2 + $50k = $250k
$250k * 2 + $100k = $600k
etc.
Code:
let baseAmount = 50000.0
let percentReturn = 200.0
let years = 10
// Calc decimal of percent.
var out: Double = 0.0
var previous: Double = 0.0
let returnPercent = percentReturn * 0.01
// Create tmp array to store values.
var tmpArray = [Double]()
// Loop through years.
for var index = 0; index < years; ++index
{
if index == 0
{
out = baseAmount
tmpArray.append(baseAmount)
}
else if index == 1
{
out = (out * returnPercent)
tmpArray.append(out)
previous = baseAmount
}
else
{
let tmp = (tmpArray.last! * returnPercent) + previous
previous = tmpArray.last!
tmpArray.append(tmp)
}
}
println(tmpArray)
Here are some ideas for improving efficiency:
Initialize your array to the appropriate size (it isn't dynamic; it is always the number of years)
Remove special cases (year 0 and 1 calculations) from the for-loop
Code:
func calculate(baseAmount: Double, percentReturn: Double, years: Int) -> [Double] {
// I prefer to return an empty array instead of nil
// so that you don't have to check for nil later
if years < 1 {
return [Double]()
}
let percentReturnAsDecimal = percentReturn * 0.01
// You know the size of the array, no need to append
var result = [Double](count: years, repeatedValue: 0.0)
result[0] = baseAmount
// No need to do this in the loop
if years > 1 {
result[1] = baseAmount * percentReturnAsDecimal
}
// Loop through years 2+
for year in 2 ..< years {
let lastYear = result[year - 1]
let yearBeforeLast = result[year - 2]
result[year] = (lastYear * percentReturnAsDecimal) + yearBeforeLast
}
return result
}
Efficiency in terms of speed I found this to be the fastest implementation of your algorithm:
let baseAmount = 50000.0
let returnPercent = 2.0
let years = 10
// you know the size of the array so you don't have to append to it and just use the subscript which is much faster
var array = [Double](count: years, repeatedValue: 0)
var previousValue = 0.0
var currentValue = baseAmount
for i in 0..<years {
array[i] = currentValue
let p2 = currentValue
currentValue = currentValue * returnPercent + previousValue
previousValue = p2
}
print(array)
I am making a game using Swift and SpriteKit where i move an object to random locations based on an array.
The array that is made up of CGPoints:
let easyArray = [CGPointMake(0,0), CGPointMake(126.6,0), CGPointMake(253.4,0), CGPointMake(0,197.5), CGPointMake(126.7,197.5), CGPointMake(253.4,197.5), CGPointMake(0,395), CGPointMake(126.7,395), CGPointMake(253.4,395)]
I use this function to generate a random number:
func randomNumber(maximum: UInt32) -> Int {
var randomNumber = arc4random_uniform(maximum)
while previousNumber == randomNumber {
randomNumber = arc4random_uniform(maximum)
}
previousNumber = randomNumber
return Int(randomNumber)
}
I used this to move the object based on the random number generated:
let greenEasy = randomNumberNew(9)
let moveSelector = SKAction.moveTo(easyArray[greenEasy], duration: 0)
selector.runAction(moveSelector)
I have done some reading online and found that the "While" condition should make it so that the same random number isn't generate twice in a row. But it still happens.
Can anyone please help me on how to make it so i don't get the same number twice in a row?
The code below doesn't random the same number.
var currentNo: UInt32 = 0
func randomNumber(maximum: UInt32) -> Int {
var randomNumber: UInt32
do {
randomNumber = (arc4random_uniform(maximum))
}while currentNo == randomNumber
currentNo = randomNumber
return Int(randomNumber)
}
I think Larme's suggestion is pretty clever, actually.
easyArray.append(easyArray.removeAtIndex(Int(arc4random_uniform(UInt32(easyArray.count)-1))))
selector.runAction(SKAction.moveTo(easyArray.last!, duration: 0))
I would recommend to not use while() loops with randomizers.
Theoretically it can cause infinite loops in worst case scenario, in more positive scenario it will just take few loops before you get desired results.
Instead I would advice to make an NSArray of all values, remove from this NSArray last randomized element and randomize any of other existing elements from such an array - that is guarantee result after only one randomize iteration.
It can be easily achieved by making NSArray category in Objective-C:
- (id) randomARC4Element
{
if(self.count > 0)
{
return [self objectAtIndex:[self randomIntBetweenMin:0 andMax:self.count-1]];
}
return nil;
}
- (int)randomIntBetweenMin:(int)minValue andMax:(int)maxValue
{
return (int)(minValue + [self randomFloat] * (maxValue - minValue));
}
- (float)randomFloat
{
return (float) arc4random() / UINT_MAX;
}
If you can use linq then you can select a random value that doesn't match the last value. Then for any left over values you can loop through and find valid places to insert them.
It's not the most efficient but it works.
public static List<int> Randomize(List<int> reps, int lastId) {
var rand = new Random();
var newReps = new List<int>();
var tempReps = new List<int>();
tempReps.AddRange(reps);
while (tempReps.Any(x => x != lastId)) {
var validReps = tempReps.FindAll(x => x != lastId);
var i = rand.Next(0, validReps.Count - 1);
newReps.Add(validReps[i]);
lastId = validReps[i];
tempReps.Remove(validReps[i]);
}
while (tempReps.Any()) {
var tempRep = tempReps.First();
bool placed = false;
for (int i = 0; i < newReps.Count; i++) {
if (newReps[i] == tempRep) {
continue;
}
else if ((i < newReps.Count - 1) && (newReps[i + 1] == tempRep)) {
continue;
}
else {
newReps.Insert(i + 1, tempRep);
placed = true;
break;
}
}
if (placed) {
tempReps.Remove(tempRep);
}
else {
throw new Exception("Unable to randomize reps");
}
}
return newReps;
}
Here is my problem.
I'm doing a little weather web app in flash.
So i read an xml feed and copose an array of data from it.
my xml is like this
<xml>
<prevision>
<date>22</date>
<hour>5</hour>
<temperature>40</temperature>
</prevision>
<prevision>
<date>22</date>
<hour>10</hour>
<temperature>44</temperature>
</prevision>
<prevision>
<date>22</date>
<hour>14</hour>
<temperature>45</temperature>
</prevision>
<prevision>
<date>22</date>
<hour>20</hour>
<temperature>37</temperature>
</prevision>
</xml>
So here is what i'm doing with my Actionscript 2
//**************************************
// Here i'm getting the current hour
var mytime = new Date();
var currenthour = mytime.getHours();
//*************************************
// Here i'm getting my XML
var myhour:Array = new Array();
var myxml:XML = new XML();
myxml.ignoreWhite = true;
myxml.onLoad = function(success)
{
dataextractor = myxml.firstChild.childNodes;
for (var j = 0; j < dataextractor.length; j++)
{
myhour.push(dataextractor[j].childNodes[1].firstChild.nodeValue);
}
// Doing this
trace(mydate);
}
// Will return this
// 5 , 10, 14 , 20
Now what i would like to do is to find and trace the entry in mydate array which have a a value that approach the most to "currenthour" variable (which is my current time i'm getting upper).
For exemple if currenthour = 11
In the case that i have myhour array equal to 5 , 10 , 14 , 20
The best match will be myhour[1]
Can someone help to do so?
myhour Array don't have a fixed number of entries.
Sometime it can be 5, 10, 14 and other time just 14,20 etc
Thanks a lot,
I'm not sure, if I got your idea. Do you need to find the nearest number to currenthour from myhour array?
That's it:
var minDiff : int = 12;
var diff : int;
var index : int = 0;
for (var i : int = 0; i < myhour.length; i++)
{
diff = Math.abs(myhour[i] - currenthour);
if (diff > 12)
diff = 24 - diff;
if (diff < minDiff)
{
minDiff = diff;
index = i;
}
}
myhour[index] is the result. Hope, I got an idea.
Imagine your numbers on a line, from 0 to 24.
Using a variable to keep track of the smallest distance between the current hours and each hour in your XML, you can find the index of the node you're looking for:
import mx.xpath.XPathAPI;
var myxml:XML = new XML();
myxml.ignoreWhite = true;
myxml.onLoad = function(loaded:Boolean) {
if (loaded) {
var hoursMin:Number = 24;
var hoursNow:Number = new Date().getUTCHours();
var hourID:Number;
var hours:Array = XPathAPI.selectNodeList(this.firstChild, "xml/prevision/hour");
var hoursNum:Number = hours.length;
for(var i:Number = 0 ; i < hoursNum; i++){
var hoursDiff:Number = Math.abs(hoursNow - parseInt(hours[i].firstChild));//look for the 'shortest distance' within 24 numbers
if(hoursDiff < hoursMin){//found the smallest current value
hoursMin = hoursDiff;//update the minimum
hourID = i;//store the node ID
}
}
trace(this.firstChild.childNodes[hourID]);//access the closest node in time
} else {
trace("XML Load Error!!");
}
}
myxml.load("feed.xml");
Here's the same thing without XPath, although I find accessing nodes a bit harder this way:
var myxml:XML = new XML();
myxml.ignoreWhite = true;
myxml.onLoad = function(loaded:Boolean) {
if (loaded) {
var hoursMin:Number = 24;
var hoursNow:Number = new Date().getUTCHours();
var hourID:Number;
var hoursNum:Number = this.firstChild.childNodes.length;
for(var i:Number = 0 ; i < hoursNum; i++){
var hoursDiff:Number = Math.abs(hoursNow - parseInt(this.firstChild.childNodes[i].childNodes[1].firstChild));//look for the 'shortest distance' within 24 numbers
if(hoursDiff < hoursMin){//found the smallest current value
hoursMin = hoursDiff;//update the minimum
hourID = i;//store the node ID
}
}
trace(this.firstChild.childNodes[hourID]);//access the closest node in time
} else {
trace("XML Load Error!!");
}
}
myxml.load("feed.xml");
HTH