Преобразовать массив Measurement () в UnitLength в тот же естественный масштаб - PullRequest
0 голосов
/ 02 ноября 2018

У меня есть массив значений UnitLength (высота набора высоты), которые я хочу визуализировать на графике. Необработанные данные хранятся в метрах, поэтому мне нужно преобразовать значения в локализованную единицу пользователя (например, футы). У Apple есть отличный API для этого:

let measurementFormatter = MeasurementFormatter()
measurementFormatter.unitOptions = .naturalScale
let measurement = Measurement(value: valueInMeters, unit: UnitLength.meters)
measurementFormatter.string(from: measurement)

Итак, теперь я преобразовал одну точку данных в натуральную шкалу. Однако мне нужно теперь преобразовать весь массив дневных точек в ТО ЖЕ единицу. Но я не знаю, какую единицу измерения использовал формат. Какая полученная единица?

В противном случае, если я преобразую все значения по отдельности, некоторые из них будут очень маленькими, поэтому натуральная шкала будет в дюймах. В конце я хочу, чтобы натуральная шкала зависела от среднего или медианного значения, чтобы большинство значений были в их естественном масштабе.

Я не мог понять, как это возможно с этим API - кроме как делать что-то глупое, как это:

let symbol = measurementFormatter.string(from: measurement).split(separator: " ").last?.lowercased()
// is there not better way to get the UnitLength from naturalScale conversion result?

if symbol == "m" {
    return .meters
}
if symbol == "ft" {
    return .feet
}
if symbol == "yd" {
    return .yards
}
if symbol == "in" {
    return .inches
}
if symbol == "cm" {
    return .centimeters
}
if symbol == "mm" {
    return .millimeters
}
if symbol == "km" {
    return .kilometers
}
if symbol == "mi" {
    return .miles
}

Но я не хочу отправлять этот код. Нет лучшего варианта?

1 Ответ

0 голосов
/ 02 ноября 2018

Давайте начнем с этого массива:

let dataPoints = [0.0001, 0.1, 1, 2, 3, 1000, 2000]

Наиболее распространенной единицей, подходящей для этого массива, будет единица для медианного значения:

let median = dataPoints.sorted(by: <)[dataPoints.count / 2]
let medianMeasurement = Measurement(value: 1700, unit: UnitLength.meters)

В следующем фрагменте мы выясним наиболее подходящую единицу. Если единица измерения меньше точки данных, то она считается естественной единицей:

let imperialUnitsNames: [UnitLength] = [.inches,
                                        .feet,
                                        .yards,
                                        .fathoms,
                                        .furlongs,
                                        .miles,
]

let imperialUnitsInMeters: [Any] = imperialUnitsNames.map { unit in
    let m = Measurement(value: 1, unit: unit).converted(to: .meters)
    return m.value
}

let zipped = zip(imperialUnitsInMeters, imperialUnitsNames)
let naturalUnit = zipped.reversed()
    .first(where: { $0.0 < median})!
    .1

Вы можете настроить возможные единицы в imperialUnitsNames.

Давайте создадим форматер измерений:

let measurementFormatter = MeasurementFormatter()
measurementFormatter.unitOptions = .providedUnit

Теперь мы готовы отформатировать dataPoints:

let measurementStrings: [String] = dataPoints.map { dataPoint in
    let measurement = Measurement(value: dataPoint, unit: UnitLength.meters)
    let newMeasurement = measurement.converted(to: naturalUnit)
    return measurementFormatter.string(from: newMeasurement)
}

print(measurementStrings) //["0 ftm", "0.055 ftm", "0.547 ftm", "1.094 ftm", "1.64 ftm", "546.807 ftm", "1,093.613 ftm"]
...