I JavaScript, hur säkerställer man att en array har åtminstone ett specifikt element och att de andra uppfyller ett annat villkor?


Givet en array som innehåller några statussträngar vill jag validera om arrayen innehåller minst en sleeping-status och sedan validera om resten av statusarna är antingen ok eller sleeping.

Så en giltig array är ['ok', 'sleeping', 'sleeping', 'ok'] och en ogiltig array skulle vara något som const states=['ready', 'working','onBreak','sleeping', 'ok', 'sleeping']

Det jag har kommit på hittills är detta:

const validArray = ['ok', 'sleeping', 'sleeping', 'ok'];

const isvalid = validArray.some( x => x === 'sleeping')

if(isValid){
  const canDoStuff = validArray.some( x => !['ok','sleeping'].includes(x))
  if(canDoStuff){
    doStuff()  
  }
}

Idealiskt skulle jag vilja validera det i en enda slinga, och inte två slingor.

Svar #1

Istället för .some för att kontrollera om det finns ett sleeping objekt, använd .includes.

Men ditt andra .some implementerar inte heller rätt logik; doStuff kommer bara att köras om det finns minst ett objekt som inte matchar. Använd .every istället och kontrollera att varje matchar:

const validate = input => (
  input.includes('sleeping') &&
  input.every(x => ['ok', 'sleeping'].includes(x))
);

console.log(validate(['ok', 'sleeping', 'sleeping', 'ok']));
console.log(validate(['ready', 'working','onBreak','sleeping', 'ok', 'sleeping']));

Svar #2

Om du vill ha den snabbaste koden, använd en enkel for-loop:

'use strict';

function isValid( array ) {
    let sleeping = false;
    for( const item of array ) {
        if( item === 'sleeping' ) {
            sleeping = true;
        } else if( item !== 'ok' ) {
            return false;
        }
    }
    return sleeping;
}

function test( array ) {
    console.log( isValid(array) ? 'Valid:' : 'Invalid:', array );
}

test( [ 'ok', 'sleeping', 'sleeping', 'ok' ] );
test( [ 'ready', 'working','onBreak','sleeping', 'ok', 'sleeping' ] );

Detta är lite mer kod än de andra svaren föreslår, men det kommer att vara snabbare än någon av dem.

Specifikt använder det accepterade svaret två loopar, inte en. Du ser inte looparna direkt eftersom de är gömda inne i .includes() och .every()-anropen.

Jag menar inte detta som en kritik av den koden - den är väldigt ren och enkel, vilket jag alltid gillar. Men när prestanda är viktigt kan det hjälpa att skriva gammaldags kod som låter dig kombinera flera loopar till en.

Självklart är det alltid klokt att göra jämförelser när prestanda kan vara viktig, så jag tog testet som @scunliffe skapade (tack!) och lade till ett test som använder denna for-loop: https://jsben.ch/vrzl1

Om du behöver stödja äldre versioner av Internet Explorer, använd en numerisk for-loop istället för for-of-loopen, och var istället för let och const.

Relaterade artiklar