Kann man in JavaScript auszuwählen lassen, wie oft eine Ziffer in einer Zahl vorkommt?

5 Antworten

Das ist natürlich möglich.

Hier ein Skript, das Zahlen mit doppelten Ziffern entfernt:

let numbers = [123, 345, 223, 456, 887, 12353, 11002, 7890];

let noDuplicateDigits = filterDuplicateDigits(numbers);

console.log(noDuplicateDigits);


function filterDuplicateDigits(numArray) {
    return numArray.filter((numberElement) => {
        //convert to string for easier analysis
        let numberString = String(numberElement);
        for (let i = 0; i < numberString.length; i++) {
            let currentDigit = numberString[i];
            //check if it's the first occurence - if it's not, it's a duplicate
            if (numberString.indexOf(currentDigit) != i) {
                //return false -> remove number
                return false;
            }
        }
        //no duplicates, keep number
        return true;
    });
}

Falls du Fragen hast, wie der Code funktioniert, gerne nachhaken.

Das Skript entfernt alle Zahlen, bei denen eine Ziffer irgendwo doppelt ist - also auch wenn die doppelte Ziffern nicht direkt hintereinander auftritt.

Falls du nur Zahlen mit aufeinanderfolgenden Ziffern entfernen willst, müsste man den Code leicht anpassen.

Update:

Nach genauerem Lesen habe ich festgestellt, dass du "mehr als 2 mal" geschrieben hast, ich habe es als "2 mal oder mehr" intepretiert, was aber nicht das gleiche ist. Hier eine Angepasste Version, bei der du sogar angeben kannst, wie oft sich eine Ziffer maximal wiederholen darf, bevor die Zahl entfernt wird:

let numbers = [123, 345, 223, 456, 887, 12353, 11002, 7890, 110112, 101021];

let noDuplicateDigits = filterRepeatedDigits(numbers, 2);

console.log(noDuplicateDigits);

function filterRepeatedDigits(numArray, maxRepetitions) {
    return numArray.filter((numberElement) => {
        //convert number to array of strings
        let digits = String(numberElement).split("");

        /*count how often each digit from 0-9 occurs
        by filtering the array to only that digit
        and checking the length*/
        for (let i = 0; i <= 9; i++) {
            if (digits.filter(digit => digit == String(i)).length > maxRepetitions) {
                return false
            }
        }

        return true;
    });
}
Woher ich das weiß:Berufserfahrung – Tätigkeit als Webentwickler in einer Digitalagentur

Da du gerne mit Ziffern arbeiten möchtest, wäre eine String Darstellung der Zahl vom Vorteil.

Eine Möglichkeit wäre, dass du zunächst das erste Zeichen (Ziffer) vom String in eine Variable speicherst. Anschließend bildest du ein Substring ohne dem ersten Zeichen.

Auf dem Substring suchst du per indexOf das erste Zeichen, welches nicht mehr im String drin ist. Wenn keine -1 zurückgegeben wird, kannst du mit einem Fehler beenden. Das Zeichen hat ein zweites Vorkommnis.

Wenn -1 zurückgegeben wurde, fängst du wieder mit dem gekürzten String von vorne an, bis der String leer ist. Eine rekursive Lösung wäre möglich. Iterativ kann auch es auch implementiert werden.

Also rekursiv in Pseudocode (fast Javascript) könnte es so aussehen:

boolean duplicates(String text) {
  if (text.isEmpty()) {
    return false
  }
  let first = text[0]
  let last = text.substring(1)
  if (last.indexOf(first) != -1) {
    return true
  }
  return duplicates(last)
}

Die Rückgabe ist true, wenn es doppelte Zeichen gibt (egal ob Ziffer oder sonstiges Zeichen) und false, wenn jedes Zeichen maximal 1x vorkommt.

Beim Syntax müsstest du nochmal schauen, ob es korrekt für JavaScript ist.

Ireeb  23.03.2024, 13:29

Hier mit JavaScript-Syntax und kombiniert mit der array.filter-Methode:

let numbers = [123, 345, 223, 456, 887, 12353, 11002, 7890];

let noDuplicateDigits = numbers.filter(numberElement => !duplicates(numberElement));

console.log(noDuplicateDigits);

function duplicates(num) {
    let text = String(num);
    if (!text) {
        return false
    }
    let first = text[0]
    let last = text.substring(1)
    if (last.indexOf(first) != -1) {
        return true
    }
    return duplicates(last)
}

Ich habe noch die Konversion von Number zu String in die Funktion gepackt, damit es im Filter-Callback übersichtlicher ist.

Habe es auch getestet, und es funktioniert.

Ausgabe ist:

[ 123, 345, 456, 7890 ]
1
Ireeb  23.03.2024, 13:45

P.S. Wie du in meinen Script siehtst, werden Datentypen bei JavaScript unter den Teppich gekehrt, und sowohl leere Strings als auch undefined oder null evaluieren zu false.

Dafür gibt es ja aber TypeScript, um Typensicherheit zu bekommen.

Und wenn ich es in TypeScript anschaue, sehe ich auch, warum du die Konversion außerhalb der Funktion hattest. Mit meinen Anpassung bekommt die Funktion ab dem ersten rekursiven Aufruf statt einer Number einen String als Parameter. Ist in JavaScript egal, in TypeScript müsste man entweder die Konversion außerhalb machen, oder als Argument für die Funktion den Typ string | number angeben, damit sie beides akzeptiert.

Das hier wäre letztere Variante in TypeScript:

function duplicates(num: number | string): boolean {
    let text = String(num);

    if (!text) {
        return false
    }
    let first = text[0]
    let last = text.substring(1)
    if (last.indexOf(first) != -1) {
        return true
    }
    return duplicates(last)
}

Der String-Konstruktor nimmt beliebige Datentypen (any) an, daher ist es egal, ob man ihm eine Number oder einen String übergibt. Mit einem String als Parameter gibt String() einfach einen identischen String zurück.

0
tide1109  23.03.2024, 13:59
@Ireeb

Ich bin verstärkt in der Java Welt ohne dem Script Suffix aktiv. Da gibt es immer feste Typen. JavaScript und TypeScript habe ich schonmal benutzt und daran nicht die größte Freude empfunden. HTML/CSS bzw. Frontend ist auch nicht so meins.

Ein meiner Version wird direkt ein String als Eingabe erwartet, sodass die Typen auch passen. Man müsste also manuell vorher die String Umwandlung ausführen.

Keine Ahnung wie JS/TS damit klar kommen, aber in z.B. Java könnte man auch die Methode überladen.

Also mit Java Syntax:

boolean duplicates(String text) {
  // wie oben
}

boolean duplicates(int number) {
  return duplicates(String.valueOf(number)); // Aufruf der Methode mit Parameter vom Typ String
}

Ich denke, dass der Fragesteller gut mit den Beispielen zurecht kommt und es nach seinen Wünschen in seine Anwendung integrieren kann.

0
Ireeb  23.03.2024, 14:17
@tide1109

Mir ist gerade noch aufgefallen, dass der Fragesteller "mehr als 2 mal" und nicht "2 mal oder mehr" geschrieben hat, also n>2 und nicht n=>2, wir haben aber beide glaube ich letzteres angenommen. Habe in meiner Antwort schon eine zweite Version hinzugefügt.

Überladen geht in TypeScript meines Wissens nach nicht.

Die Vorgehensweise wäre hier denke ich eben beide Typen zu erlauben, und falls notwendig, Type Guards zu verwenden. Type Guards sind glaube ich mit Code einfacher zu erklären:

function example(param1: string | number) {
    
  /*hier hat param1 den Typ string | number.
  eine Funktion, die z.B. nur string als Parameter hat,
  könnte nicht mit param1 aufgerufen werden. */


  if (typeof param1 == "string") {


    /*TypeScript erkennt, dass wir den Datentyp eingegrenzt haben
    daher hat param1 in diesem Block nun den Typ string */


  }
}

Diesen Aufbau nennt man in TypeScript eben einen Type Guard. Innerhalb des if-Blocks wird param1 als String behandelt, kann also auch mit Funktionen verwendet werden, die einen String erwarten.

Ich verstehe völlig, wenn man mit JavaScript einen Anfall bekommt, weil keine Datentypen deklariert sind und es gerne mal einfach heimlich Datentypen umwandelt bzw. castet.

Ich finde allerdings, das TypeScript sehr angenehm ist, wenn man sich genug damit auseinandersetzt. Es hat ein paar eigene Konzepte, auf die man sich einlassen muss, aber ich finde es hat eine gute Balance zwischen Typensicherheit und Flexibilität.

0
Von Experte Babelfish bestätigt

Ich mach mir nicht die Mühe ein eigenes Muster zu bauen und greife den Code meiner Vorgänger auf.

Ich bin kein JavaScript-Experte deshalb vergebt mir eventuelle Nachlässigkeiten bezüglich der modernen Syntax.

Was ich aber gut kann ist RegEx (egal in welcher Sprache) und diese Aufgabe ist geradezu ein Paradebeispiel für ein minimalistisches Regex:

ich bin wie auch @Ireeb dem mehr als 2mal auf den Leim gegangen also eine korrektur

(\d)(\d*\1){2,}

  • (/d) Capture-Gruppe 1 nimmt eine Ziffer auf
  • ( beginn Gruppe 2
  • \d* gefolgt von keine oder mehrere beliebige Ziffern
  • \1 Inhalt der Capture-Gruppe 1
  • ) {2,} die 2. Guppe mindestens 2mal

reagiert: 666, 57779, 110012, 6787227, 110112, 101021...

(\d)\1{2,}

  • (/d) Capture-Gruppe 1 nimmt eine Ziffer auf
  • \1{2,} Inhalt der Capture-Gruppe 1 mindestens 2mal

reagiert: 666, 57779...

ich mache mir auch nicht die Mühe nach dem Vergleich true oder false exlizit zurückzugeben. pattern.test(string) liefert automatisch das zutreffende Boolean.

let numbers = [123, 223, 456, 666, 12353, 57779, 110012, 7890, 6787227, 110112, 101021];


let noDuplicateDigits = filterDuplicateDigits(numbers);
console.log(noDuplicateDigits);


let noConsecutiveDigits = filterConsecutiveDigits(numbers);
console.log(noConsecutiveDigits);


//wenn eine Ziffer mehr als 2mal in einer Zahl vorkommt, aber  andere dazwischen  stehen können:
// 666, 110012, 6787227, 110112, 101021  werden   ausgeschlossen
function filterDuplicateDigits(numArray) {
    return numArray.filter((numberElement) => {
        let numberString = String(numberElement);
        return !(/(\d)(\d*\1){2,}/g.test(numberString));
          //Gegenprobe:
        //return (/(\d)(\d*\1){2,}/g.test(numberString));
    })
}


//wenn mehr  als 2 gleiche  Ziffern aufeinander  folgen:
//666, 57779  werden ausgeschlossen
function filterConsecutiveDigits(numArray) {
    return numArray.filter((numberElement) => {
        let numberString = String(numberElement);
        return !(/(\d)\1{2,}/g.test(numberString));
        //Gegenprobe:
        //return (/(\d)\1{2,}/g.test(numberString));
    })
}

Eine Möglichkeit wäre so:

let values = [ 1, 3, 8, 22, 344, 25553, 42, 834828, 211 ];

let result = values.filter((value) => {
  const strValue = value.toString();
	
  for (const num of strValue.split('')) {
    if (strValue.match(`(.*${num}.*){3}`)) {
      return false;
    }
  }
	
  return true;
});

console.log(result);

Ausgabe:

[ 1, 3, 8, 22, 344, 42, 211 ]
Woher ich das weiß:Berufserfahrung – Entwickle Webseiten seit über 25 Jahren.
Erzesel  23.03.2024, 19:51

...aber wenn du schun mit RegEx arbeitest, warum zelegst du den string erstmal von hand und klapperst jedes einzelne Digit einzeln ab?

(\d)\1{2,} macht das gleiche automatisch

Die erste Gruppe "fängt" Digit für Digit , danach wird (jeweils) geprüft ob der Inhalt der Gruppe noch mindestens 2mal folgt...

das if brauchst du nicht bei Verwendung von pattern.test(string)

1

1. Iteriere über jede Zahl in der Liste

2. Ziffern zählen bzw. Auf Duplikate prüfen.

Wo ist denn das Problem? Wenn du strings benutzt ist das ganz simpel.

Woher ich das weiß:Studium / Ausbildung – Informatikstudium
DocteurTiziano3 
Fragesteller
 23.03.2024, 12:55

Das Problem sind mangelnde Kenntnisse. Ich kenne noch nicht die notwendigen Befehle um dies zu machen.

0
triopasi  23.03.2024, 13:01
@DocteurTiziano3

Dann solltest du zu erst recherchieren wir man eine Liste iterierrt. Dann solltest du dir überlegen wie man bei einem Element jeweils prüfen könnte welche Ziffern es halt und wie oft diese ggf. auftritt.

0