DEV Community

Cover image for pętla for vs chaining vs .reduce()
Bartosz Zagrodzki
Bartosz Zagrodzki

Posted on

pętla for vs chaining vs .reduce()

Przetłumaczona wersja artykułu Kenta C. Doddsa

Jedną z operacji w trakcie przenoszenia mojego internetowego życia, było pobranie wszystkich zdjęć z Google Photos. Uznałem, że potrzebuję zmiany ich organizacji, toteż napisałem do tego prosty skrypt. To, co robił nie jest istotne dla tego artykułu, więc nie będę opisywał szczegółów (tutaj cała sytuacja, jeśli chcesz o tym poczytać). Oto fragment o którym chciałbym coś powiedzieć (zredagowany dla czytelności):

const lines = execSync(`find "${searchPath}" -type f`).toString().split('\n')

const commands = lines
  .map(f => f.trim())
  .filter(Boolean)
  .map(file => {
    const destFile = getDestFile(file)
    const destFileDir = path.dirname(destFile)
    return `mkdir -p "${destFileDir}" && mv "${file}" "${destFile}"`
  })

commands.forEach(command => execSync(command))
Enter fullscreen mode Exit fullscreen mode

Zasadniczo wszystko, co się tutaj dzieje, to użycie polecenia find, aby znaleźć listę plików w katalogu, a następnie rozdzielenie wyniku na wiersze, pozbycie się białych znaków, mapowanie ich na polecenia, a później wykonanie tych poleceń.

Udostępniłem ten skrypt na Twitterze i kilka osób krytykowało to, że nie użyłem metody .reduce(). Jestem prawie pewien, że sugerowali to jako wskazówkę do optymalizacji, ponieważ możemy dzięki temu zmniejszyć liczbę pętli po tablicy.

Żeby było jasne, było tam około 50 tysięcy pozycji, więc zdecydowanie więcej, niż zazwyczaj mamy przy tworzeniu interfejsu. Chciałbym jednak zwrócić uwagę, że w przypadku jednorazowych skryptów wydajność nie jest aż tak ważna (chyba, że to co robisz jest naprawdę obciążające). W moim przypadku działało to bardzo szybko. Powolna część nie polegała na wielokrotnym iterowaniu po tablicy elementów, ale na uruchamianiu poleceń.

Inni mówili, bym używał API od Node-a lub modułów open source z npm, aby ułatwić uruchamianie tych skryptów, ponieważ "prawdopodobnie byłoby to szybsze i działałoby na różnych platformach". Ponownie, najpewniej mają rację, ale w przypadku jednorazowych skryptów, które są "wystarczająco szybkie" te rzeczy nie mają znaczenia. Jest to klasyczny przykład nakładania nieistotnych ograniczeń na program, co skutkuje bardziej skomplikowanym rozwiązaniem.

W każdym razie, chciałem przyjrzeć się kwestii użycia .reduce() zamiast .map() lub .filter().

.reduce()

Oto jak wyglądałby ten sam kod z użyciem .reduce():

const commands = lines.reduce((accumulator, line) => {
  let file = line.trim()
  if (file) {
    const destFile = getDestFile(file)
    const destFileDir = path.dirname(destFile)
    accumulator.push(`mkdir -p "${destFileDir}" && mv "${file}" "${destFile}"`)
  }
  return accumulator
}, [])
Enter fullscreen mode Exit fullscreen mode

Nie jestem jedną z tych osób, które uważają, że używanie .reduce() jest złe
(sprawdź ten wątek, po więcej ciekawych przykładów użycia .reduce()), ale myślę, że jestem w stanie rozpoznać, kiedy kod jest prostszy/bardziej skomplikowany i muszę przyznać, że przykład z użyciem chaining-u jest zdecydowanie prostszy.

Pętla

Szczerze, używam metod tablicowych tak długo, że potrzebuję chwili by napisać to z użyciem pętli. Jedną chwileczkę...

const commands = []
for (let index = 0; index < lines.length; index++) {
  const line = lines[index]
  const file = line.trim()
  if (file) {
    const destFile = getDestFile(file)
    const destFileDir = path.dirname(destFile)
    commands.push(`mkdir -p "${destFileDir}" && mv "${file}" "${destFile}"`)
  }
}
Enter fullscreen mode Exit fullscreen mode

To też nie jest dużo prostsze.

EDIT: ALE CZEKAJ! Możemy to uprościć za pomocą for...of!

const commands = []
for (const line of lines) {
  const file = line.trim()
  if (!file) continue

  const destFile = getDestFile(file)
  const destFileDir = path.dirname(destFile)
  commands.push(`mkdir -p "${destFileDir}" && mv "${file}" "${destFile}"`)
}
Enter fullscreen mode Exit fullscreen mode

Osobiście uważam, że nie jest to dużo lepsze niż tradycyjna pętla, ale sądzę, że jest całkiem proste. Niektórzy uważają pętle za zbyt "imperatywne", ale niekiedy są przydatne.

Podsumowanie

Często będę wybierać pomiędzy chaining-iem, a pętlą for...of. Jeśli pojawi się problem z wielokrotnym iterowaniem po tablicy to z pewnością zdecyduję się na for...of.

Osobiście nie używam często metody .reduce(), ale czasami ją wykorzystuję. Zdaję sobie sprawę, jak subiektywnie to brzmi, ale tak duża część programowania jest subiektywna 🤷‍♂️

Daj mi znać co o tym myślisz!

Top comments (0)