Произвольная замена с использованием Swift

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

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<Integer>();
    for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5)
            itemsToRemove.add(i);
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}
2
задан Daniel 15 January 2019 в 16:06
поделиться

8 ответов

Аналогично Заменить определенные символы в строке , вы можете отобразить каждый символ и объединить результат в строку. Но теперь вы должны следить за (оставшимися) номерами непробельных символов и (оставшимися) номерами символов, которые должны отображаться. Для каждого (не пробел) символа произвольно решается, отображать ли его (сохранять) или заменить его подчеркиванием.

let s = "Hello playground"
let factor = 0.25

var n = s.filter({ [110] != " " }).count  // # of non-space characters
var m = lrint(factor * Double(n))      // # of characters to display

let t = String(s.map { c -> Character in
    if c == " " {
        // Preserve space
        return " "
    } else if Int.random(in: 0..<n) < m {
        // Keep
        m -= 1
        n -= 1
        return c
    } else {
        // Replace
        n -= 1
        return "_"
    }
})

print(t) // _e_l_ ______o_n_
0
ответ дан Martin R 15 January 2019 в 16:06
поделиться

Возможное решение:

var str = "Hello playground"
print("Before: \(str)")
do {
    let regex = try NSRegularExpression(pattern: "\\S", options: [])
    let matches = regex.matches(in: str, options: [], range: NSRange(location: 0, length: str.utf16.count))

    //Retrieve 1/4 elements of the string
    let randomElementsToReplace = matches.shuffled().dropLast(matches.count * 1/4)

    matches.forEach({ (aMatch) in
        if randomElementsToReplace.first(where: { [110].range == aMatch.range } ) != nil {
            str.replaceSubrange(Range(aMatch.range, in: str)!, with: "_")
        } else {
            //Do nothing because that's the one we are keeping as such
        }
    })
    print("After: \(str)")
} catch {
    print("Error while creating regex: \(error)")
}

Идея, лежащая в его основе: использовать тот же шаблон регулярного выражения, что и тот, который вы использовали.
Возьмите в него n элементов (в вашем случае 1/4)
Замените все символы, которых нет в этом коротком списке.

Теперь, когда у вас есть идея, стало еще проще заменить цикл for на

for aMatch in randomElementsToReplace {
    str.replaceSubrange(Range(aMatch.range, in: str)!, with: "_")
}

Благодаря комментарию @Martin R за указание на это.

Вывод (сделано 10 раз):

[112]gt;Before: Hello playground
[112]gt;After: ____o ___y____n_
[112]gt;Before: Hello playground
[112]gt;After: _el__ _______u__
[112]gt;Before: Hello playground
[112]gt;After: _e___ ____g___n_
[112]gt;Before: Hello playground
[112]gt;After: H___o __a_______
[112]gt;Before: Hello playground
[112]gt;After: H___o _______u__
[112]gt;Before: Hello playground
[112]gt;After: __l__ _____ro___
[112]gt;Before: Hello playground
[112]gt;After: H____ p________d
[112]gt;Before: Hello playground
[112]gt;After: H_l__ _l________
[112]gt;Before: Hello playground
[112]gt;After: _____ p____r__n_
[112]gt;Before: Hello playground
[112]gt;After: H___o _____r____
[112]gt;Before: Hello playground
[112]gt;After: __l__ ___y____n_

Вы увидите, что есть небольшая разница с ожидаемым результатом, потому что matches.count == 15, поэтому 1/4 из них должны быть чем? Вам решать сделать правильный расчет в соответствии с вашими потребностями (округлить? И т. Д.), Поскольку вы не указали его.

Обратите внимание, что если вы не хотите округлять, вы также можете сделать обратное, использовать рандомед, чтобы тот не заменил, и тогда раунд может сыграть в вашу пользу.

0
ответ дан Larme 15 January 2019 в 16:06
поделиться

Я только что предложил следующее решение:

func generateMyString(string: String) -> String {
    let percentage = 0.25

    let numberOfCharsToReplace = Int(floor(Double(string.count) * percentage))

    let generatedString = stride(from: 0, to: string.count, by: 1).map { index -> String in
        return string[string.index(string.startIndex, offsetBy: index)] == " " ? " " : "_"
    }.joined()

    var newString = generatedString
    for i in generateNumbers(repetitions: numberOfCharsToReplace, maxValue: string.count - 1) {
        var newStringArray = Array(newString)
        newStringArray[i] = Array(string)[i]

        newString = String(newStringArray)
    }

    return newString
}

func generateNumbers(repetitions: Int, maxValue: Int) -> [Int] {
    guard maxValue >= repetitions else {
        fatalError("maxValue must be >= repetitions for the numbers to be unique")
    }

    var numbers = [Int]()

    for _ in 0..<repetitions {
        var n: Int
        repeat {
            n = Int.random(in: 1...maxValue)
        } while numbers.contains(n)
        numbers.append(n)
    }

    return numbers
}

Вывод:

let str = "Hello playground"
print(generateMyString(string: str)) // ___lo _l_______d
0
ответ дан Ahmad F 15 January 2019 в 16:06
поделиться

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

  1. создает список всех непробельных индексов
  2. удаляет первые 25% случайных элементов из этого списка
  3. пройти через все символы и заменить все, чей индекс является частью списка из # 2, подчеркиванием

Код может выглядеть примерно так:

func underscorize(_ str: String, factor: Double) -> String {
    // making sure we have a factor between 0 and 1
    let factor = max(0, min(1, factor))
    let nonSpaceIndices = str.enumerated().compactMap { [110].1 == " " ? nil : [110].0 }
    let replaceIndices = nonSpaceIndices.shuffled().dropFirst(Int(Double(str.count) * factor))
    return String(str.enumerated().map { replaceIndices.contains([110].0) ? "_" : [110].1 })
}

let str = "Hello playground"
print(underscorize(str, factor: 0.25))
[ 118] Пример результатов:

____o p_ay______
____o p__y____n_
_el_o p_________
0
ответ дан Cristik 15 January 2019 в 16:06
поделиться

Этот метод создает массив bools, который определяет, какие символы будут сохранены, а какие будут заменены с помощью встроенной функции shuffled.

let string = "Hello playground"
let charsToKeep = string.count / 4
let bools = (Array<Bool>(repeating: true, count: charsToKeep) 
           + Array<Bool>(repeating: false, count: string.count - charsToKeep)).shuffled()

let output = zip(string, bools).map
{
    char, bool in
    return bool ? char : "_"
}

print(String(output))

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

Вот версия, которая имеет дело с пробелами.

let string = "Hello playground and stackoverflow"
let nonSpaces = string.filter{ [111] != " " }.count

let bools = (Array<Bool>(repeating: true, count: nonSpaces / 4) + Array<Bool>(repeating: false, count: nonSpaces - nonSpaces / 4)).shuffled()

var nextBool = bools.makeIterator()
let output = string.map
{
    char in
    return char == " " ? " " : (nextBool.next()! ? char : "_")
}

print(String(output))

// Hel__ __________ a__ __a____e____w
// ___l_ _l__g_____ _n_ __a_____r__o_
0
ответ дан JeremyP 15 January 2019 в 16:06
поделиться

Решение, которое сохраняет пробелы и пунктуацию нетронутыми.
Мы найдем их с помощью метода расширения indiciesOfPuntationBlanks() -> [Int]. замена случайно выбранных символов будет выполнена с помощью blankOut(percentage: Double) -> String

extension String {
    func indiciesOfPuntationBlanks() -> [Int] {
        let charSet = CharacterSet.punctuationCharacters.union(.whitespaces)
        var indices = [Int]()

        var searchStartIndex = self.startIndex
        while searchStartIndex < self.endIndex,
            let range = self.rangeOfCharacter(from: charSet, options: [], range: searchStartIndex ..< self.endIndex),
            !range.isEmpty
        {
            let index = distance(from: self.startIndex, to: range.lowerBound)
            indices.append(index)
            searchStartIndex = range.upperBound
        }

        return indices
    }


    func blankOut(percentage: Double) -> String {
        var result = self
        let blankIndicies = result.indiciesOfPuntationBlanks()
        let allNonBlankIndicies = Set(0 ..< result.count).subtracting(blankIndicies).shuffled()
        let picked = allNonBlankIndicies.prefix(Int(Double(allNonBlankIndicies.count) * percentage))

        picked.forEach { (idx) in
            let start = result.index(result.startIndex, offsetBy: idx);
            let end = result.index(result.startIndex, offsetBy: idx + 1);
            result.replaceSubrange(start ..< end, with: "_")
        }

        return result
    }
}

Использование:

let str = "Hello, World!"

for _ in 0 ..< 10 {
    print(str.blankOut(percentage: 0.75))
}

Вывод:

____o, _or__!
_e___, __rl_!
_e__o, __r__!
H____, W_r__!
H_l__, W____!
_____, _or_d!
_e_lo, _____!
_____, _orl_!
_____, _or_d!
___l_, W___d!

[ 1112] То же решение, но можно настроить строку для исключения и наборы символов, которые следует игнорировать

extension String {
    func indicies(with charSets:[CharacterSet]) -> [Int] {
        var indices = [Int]()

        let combinedCahrSet: CharacterSet = charSets.reduce(.init()) { [113].union($1) }
        var searchStartIndex = self.startIndex
        while searchStartIndex < self.endIndex,
            let range = self.rangeOfCharacter(from: combinedCahrSet, options: [], range: searchStartIndex ..< self.endIndex),
            !range.isEmpty
        {
            let index = distance(from: self.startIndex, to: range.lowerBound)
            indices.append(index)
            searchStartIndex = range.upperBound
        }

        return indices
    }

    func blankOut(percentage: Double, with blankOutString: String = "_", ignore charSets: [CharacterSet] = [.punctuationCharacters, .whitespaces]) -> String {
        var result = self
        let blankIndicies = result.indicies(with: charSets)
        let allNonBlankIndicies = Set(0 ..< result.count).subtracting(blankIndicies).shuffled()
        let picked = allNonBlankIndicies.prefix(Int(Double(allNonBlankIndicies.count) * percentage))

        picked.forEach { (idx) in
            let start = result.index(result.startIndex, offsetBy: idx);
            let end = result.index(result.startIndex, offsetBy: idx + 1);
            result.replaceSubrange(start ..< end, with: blankOutString)
        }

        return result
    }
}

Использование:

let str = "Hello, World!"

for _ in 0 ..< 10 {
    print(str.blankOut(percentage: 0.75))
}
print("--------------------")

for _ in 0 ..< 10 {
    print(str.blankOut(percentage: 0.75, with:"x", ignore: [.punctuationCharacters]))
}

print("--------------------")

for _ in 0 ..< 10 {
    print(str.blankOut(percentage: 0.75, with:"*", ignore: []))
}

Выход: [1114 ]

_el_o, _____!
__llo, _____!
He__o, _____!
_e___, W_r__!
_el_o, _____!
_el__, ___l_!
_e___, __rl_!
_e__o, _o___!
H____, Wo___!
H____, __rl_!
--------------------
xxxlx,xWxrxx!
xxxxx,xxorxd!
Hxxxx,xWxrxx!
xxxxx, xoxlx!
Hxllx,xxxxxx!
xelxx,xxoxxx!
Hxxxx,xWxxxd!
Hxxxo,xxxxxd!
Hxxxx,xxorxx!
Hxxxx, Wxxxx!
--------------------
***l***Wo**d*
*e**o**W**l**
***lo**Wo****
*el*****or***
H****,****ld*
***l*, **r***
*el*o* ******
*e*lo*******!
H*l****W***d*
H****, **r***
0
ответ дан vikingosegundo 15 January 2019 в 16:06
поделиться

Идея та же, что и у описанных выше методов, только с немного меньшим количеством кода.

var str = "Hello playground"

print(randomString(str))
 print(randomString(str))
// counting whitespace as a random factor
func randomString(_ str: String) -> String{
let strlen = str.count
let effectiveCount = Int(Double(strlen) * 0.25)
let shuffled = (0..<strlen).shuffled()
return String(str.enumerated().map{
      shuffled[[110].0] < effectiveCount || ([110].1) == " " ? ([110].1) : "_"
 })}


//___l_ _l__gr____
//H____ p___g____d


func underscorize(_ str: String) -> String{
let effectiveStrlen  = str.filter{[110] != " "}.count
let effectiveCount = Int(floor(Double(effectiveStrlen) * 0.25))
let shuffled = (0..<effectiveStrlen).shuffled()
return String((str.reduce(into: ([],0)) {
  [110].0.append(shuffled[[110].1] <= effectiveCount || $1 == " "  ?  $1 : "_" )
  [110].1 += ($1 == " ") ? 0 : 1}).0)
 }


 print(underscorize(str))
 print(underscorize(str))

//__l__ pl__g_____
//___lo _l_______d
0
ответ дан E.Coms 15 January 2019 в 16:06
поделиться

Другой возможный подход состоит в том, чтобы генерировать случайные индексы для данной строки и затем заменять символы в этих индексах:

var str = "Hello, playground"

let indexes: [Int] = Array(0..<str.count)

let randomIndexes = Array(indexes.shuffled()[0..<(str.count / 4)])

for index in randomIndexes {
    let start = str.index(str.startIndex, offsetBy: index)
    let end = str.index(str.startIndex, offsetBy: index+1)
    str.replaceSubrange(start..<end, with: "_")
}

print(str)

Если вы поместите это в расширение для String, это будет выглядеть так: [113 ]

extension String {

    func randomUnderscores(factor: Double) -> String {
        let indexes: [Int] = Array(0..<count)
        let endIndexes = Int(Double(count) * factor)
        let randomIndexes = Array(indexes.shuffled()[0..<endIndexes])

        var randomized = self

        for index in randomIndexes {
            let start = randomized.index(startIndex, offsetBy: index)
            let end = randomized.index(startIndex, offsetBy: index+1)
            randomized.replaceSubrange(start..<end, with: "_")
        }

        return randomized
    }
}

print(str.randomUnderscores(factor: 0.25))
0
ответ дан Marcel 15 January 2019 в 16:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: