ar
Feedback
ToCode

ToCode

الذهاب إلى القناة على Telegram

טיפים קצרים למתכנתים מאת ינון פרק

إظهار المزيد
1 418
المشتركون
-224 ساعات
-37 أيام
-630 أيام
أرشيف المشاركات
ToCode
1 418
מה מודדים בהתלבטות בין שני קורסים יש דברים שמאוד קל למדוד: אפשר להשוות את אורך הקורסים, את המחיר, את המחיר לשעה, אפשר להסתכל על עבודות של בוגרים אחרים ולמדוד כמה הפרויקט נראה מעניין ואפילו אפשר להשוות כמה תארים או כמה שנות ניסיון יש למרצים. אבל את הדברים שחשוב למדוד הרבה יותר קשה לזהות: חשוב למדוד כמה מוטיבציה הקורס ייתן לי כדי להמשיך ללמוד גם אחריו. חשוב למדוד כמה מהר הקורס יאפשר לי להתחיל לעבוד לבד, וכמה רחוק אוכל להגיע בעזרת הרעיונות והעקרונות שאלמד שם. חשוב למדוד כמה גבוה הסיכוי שלי למצוא עבודה אחרי הקורס והאם המרצים באמת יוכלו להבין את המצב שלי ולתת לי עצות רלוונטיות עבורי. בשביל למדוד את הדברים החשובים לא מספיק להסתכל בסילבוס - צריך ממש לדבר עם המרצים ועם בוגרים, להשתתף בשיעורי ניסיון ואולי אפילו ללמוד מראש בבית כדי להגיע עם מספיק רקע ולשאול את השאלות הנכונות. רק בגלל שקל למדוד משהו לא אומר שהוא נותן אינדיקציה טובה להצלחה.

ToCode
1 418
ואפשר לראות את התוצאה בקישור: https://ynonp.github.io/echarts-birth-demo/ וגם את קוד הפרויקט המלא בגיטהאב: https://github.com/ynonp/echarts-birth-demo מעבר לנוחות העבודה עם echarts, דוגמה זו הזכירה לי כמה קל לכתוב היום אתר סטטי בלי שלב בנייה ולאחסן אותו על Github Pages. בזכות התמיכה המובנית ב ES Modules ובטעינת קבצי JSON מתוך ES Modules אנחנו מקבלים בחינם הרבה מהדברים בשבילם היינו צריכים בעבר שלב בנייה.

ToCode
1 418
דוגמת Echarts - ניתוח נתוני לידות ספריית Echarts נותנת דרך קלה מאוד ליצור תרשימים וגרפים ב Web. בשילוב עם מאגר הנתונים הישראלי הממשלתי אפשר להתנסות בקלות בניתוח נתונים ושימוש בגרפים. הנה דוגמה קצרה עם קובץ של נתוני לידות שלקחתי מהקישור הזה: https://data.gov.il/dataset/birth-data איך נראה המידע מאגר הנתונים כולל קובץ CSV שמכיל את העמודות: חודש לידה, גיל האם, מספר עוברים, שבוע לידה, מין התינוק ומשקל. יש פה הרבה עם מה לעבוד ואני בחרתי בשביל הדוגמה לבנות גרף עמודות שיראה את התפלגות הגילאים של היולדות בכל חודש בשנת 2014 (לה מתאים הקובץ). בשביל לעבוד עם הנתונים התחלתי עם הספריה csvkit של פייתון והפכתי את קובץ הנתונים ל JSON באמצעות הפקודה:
in2csv --encoding "utf-8" births.csv | csvjson > births.json
אלה שלושת האוביקטים הראשונים מהקובץ בשביל הדוגמה:
[
  {
    "birth_month": 1.0,
    "mother_age": "<18",
    "parity": "1",
    "gestation_week": "37-41",
    "birth_sex": "F",
    "birth_weight": "2500-2599"
  },
  {
    "birth_month": 1.0,
    "mother_age": "<18",
    "parity": "1",
    "gestation_week": "37-41",
    "birth_sex": "F",
    "birth_weight": "2500-2599"
  },
  {
    "birth_month": 1.0,
    "mother_age": "<18",
    "parity": "1",
    "gestation_week": "37-41",
    "birth_sex": "F",
    "birth_weight": "2500-2599"
  }
]
הכנת הנתונים לגרף עכשיו אנחנו מגיעים לחלק המעניין וצריכים לארגן את המידע כך שיתאים ל ECharts. אני רוצה להציג גרף עמודות מאוזן. בגרף עמודות פשוט אנחנו מציגים ערכים מתוך סידרה כלומר היינו צריכים רשימה של מספרים. אני רוצה להציג בכל עמודה את הלידות מחולקות לפי גיל האם, ולכן אחלק את הנתונים למספר רשימות, רשימה לכל קבוצת גילאים:
const dataByAge = _.chain(data).groupBy('mother_age').mapValues(
  row => _.chain(row).groupBy('birth_month').mapValues(v => v.length).value()
).value()

const series = _.toPairs(dataByAge).map(([mother_age, entries]) => ({
    name: mother_age,
    type: "bar",
    stack: "total",
    label: {
      show: true,
    },
    emphasis: {
      focus: "series",
    },
    data: Object.values(entries),
  })
)
התוצאה היא רשימה של אוביקטים, כלומר של סדרות, כאשר שם כל סידרה הוא קבוצת הגילאים של האם והערכים (במפתח data) הם רשימה בה כל איבר הוא מספר הלידות בקבוצת הגיל הנתונה בחודש אחר, כלומר האיבר הראשון הוא כמה לידות היו בינואר בקבוצת גיל זו, אחרי זה כמה לידות היו בפברואר וכך הלאה. סיכום והקוד המלא קוד ה JavaScript המלא לציור הגרף הוא:
import data from './births.json' with { type: "json" };
import _ from 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/+esm'

window.data = data;
const months = {
  1: 'January',
  2: 'February',
  3: 'March',
  4: 'April',
  5: 'May',
  6: 'June',
  7: 'July',
  8: 'August',
  9: 'September',
  10: 'October',
  11: 'November',
  12: 'December'
};

var dom = document.getElementById("chart-container");
var myChart = echarts.init(dom, null, {
  renderer: "canvas",
  useDirtyRect: false,
});
var app = {};
var option;

const dataByAge = _.chain(data).groupBy('mother_age').mapValues(
  row => _.chain(row).groupBy('birth_month').mapValues(v => v.length).value()
).value()

const series = _.toPairs(dataByAge).map(([mother_age, entries]) => ({
    name: mother_age,
    type: "bar",
    stack: "total",
    label: {
      show: true,
    },
    emphasis: {
      focus: "series",
    },
    data: Object.values(entries),
  })
)

option = {
  tooltip: {
    trigger: "axis",    
    axisPointer: {
      // Use axis to trigger tooltip
      type: "shadow", // 'shadow' as default; can also be 'line' or 'shadow'
    },
  },
  legend: {},
  grid: {
    left: "3%",
    right: "4%",
    bottom: "3%",
    containLabel: true,
  },
  xAxis: {
    type: "value",
  },
  yAxis: {
    type: "category",
    data: Object.values(months),
  },
  series: series,
};

if (option && typeof option === "object") {
  myChart.setOption(option);
}

window.addEventListener("resize", myChart.resize);

ToCode
1 418
כשהשפה עובדת אתך - פיתרון Advent Of Code 2024 יום ראשון ב Ruby האידאולוגיה של רובי היא לתת למתכנתים שפה שפשוט יש בה הכל, כי תכל'ס יותר כיף להשתמש בפונקציה מובנית בשפה מאשר להעתיק מימוש משעמם מ Chat GPT. הנה שתי דוגמאות מפיתרון היום הראשון של Advent Of Code האחרון לפונקציות שקשה למצוא במקומות אחרים. חלק 1 - הפונקציה transpose הקלט של החלק הראשון בתרגיל מורכב משני טורים:
3   4
4   3
2   5
1   3
3   9
3   3
והמשימה שלנו היא למיין כל טור בנפרד ואז להתאים את המספרים מהטור השמאלי לטור הימני מהקטן לגדול בשני הטורים, ובסוף למצוא את ההפרש בערך מוחלט של כל זוג. לדוגמה המספר הקטן ביותר בטור השמאלי הוא 1 והקטן ביותר בטור הימני הוא 3 ולכן נתאים את אחד ל-3 ונקבל שההפרש הוא 2. אם תמשיכו את זה לכל המספרים ותסכמו את ההפרשים תגיעו ל 11. האתגר בחלק הזה הוא ששפות תכנות בדרך כלל נותנות לנו לקרוא קלט בשורות, וקל להפעיל split על שורת קלט בשביל לחלק אותה למערך לפי הרווחים - אבל איך עושים את זה על עמודה? הפונקציה transpose של מערך ברובי מציעה פיתרון קל, זה הקוד:
filename = ARGV[0]
left, right = File.foreach(filename).map(&:split).transpose

left = left.map(&:to_i).sort
right = right.map(&:to_i).sort

puts (left.each_with_index.map do |value, index|
  (value - right[index]).abs
end).sum
פונקציית transpose לוקחת מערך של זוגות והופכת אותו לשתי רשימות, הרשימה הראשונה של כל "הראשונים" והרשימה השנייה של כל "השניים". בפועל קריאת הקובץ לשני טורים היא בסך הכל השורה:
left, right = File.foreach(filename).map(&:split).transpose
חלק 2 - הפונקציה tally הפונקציה השניה לפוסט נקראת tally והיא מתודה של מערך שמחזירה hash עם מספר המופעים של כל איבר במערך. היא קיימת גם במקומות אחרים אבל קצת יותר קשה להגיע אליה שם, לדוגמה בפייתון היא נקראת Counter שמורה במודול נפרד. ב JavaScript כבר נצטרך את lodash בשביל זה (או לכתוב משהו לבד). בחלק השני של התרגיל התבקשנו לכפול כל מספר מהטור השמאלי במספר הפעמים שהוא מופיע בטור הימני. זה הקוד עם tally:
filename = ARGV[0]
left, right = File.foreach(filename).map {it.split.map(&:to_i)}.transpose
count = right.tally

puts (left.map do |value|
  value * count.fetch(value, 0)
end).sum
הפעם בגלל שלא היה מיון העברתי את to_i פנימה לשורת הקריאה יחד עם ה split, והשתמשתי בכתיב ה it החדש של רובי 3.4.

ToCode
1 418
חידת Vue משתנים ריאקטיביים נתון קוד Vue הבא:
<script setup>
import { ref } from 'vue'
const init = {count: 0}
const item = ref(init)

function inc() { item.value.count++ }
function reset() { item.value = init }
</script>

<template>
  <p>{{ item.count }}</p>
  <button @click="inc">+1</button>
  <button @click="reset">Reset</button>
</template>
משתמש לוחץ כמה פעמים על כפתור הפלוס ואז על כפתור ה Reset. האם הערך בתיבה מתאפס? למה? וכן הכנתי לכם את זה כבר בלייב שתוכלו לבדוק כאן: דוגמת לייב הסבר הכפתור לא מאפס את הערך, או יותר נכון הוא מאפס אבל לא לערך שרצינו. זה מה שקורה: 1. פקודת ה ref יוצרת קישור ל"דבר" שהיא קיבלה בסוגריים. מאחר והדבר הזה הוא אוביקט יהיה לנו פרוקסי שמחזיק אוביקט, אבל ההעתקה היא שטחית. 2. שינוי הערך של item הוא בעצם שינוי הערך של init, כי item הוא בסך הכל פרוקסי ריאקטיבי ל init. 3. כשלוחצים איפוס הערך של init מועתק ל item, אבל זה בסך הכל העתקה של משתנה לעצמו ולכן הערך של item לא משתנה. זה מבלבל כי הרבה פעמים אנחנו מפעילים את ref על מספרים או מחרוזות. שם העתקה שטחית יוצרת עדיין עותק של אותו מספר או מחרוזת ולכן שינוי של item לא ישפיע על init, כלומר קוד כזה יעבוד בלי בעיה:
const init = 0;
const count = ref(init);

function inc() { count.value++ }
function reset() { count.value = init }
מסיבה זו אגב אני מעדיף להשתמש ב reactive על אוביקטים ו ref על משתנים פשוטים. ההבדל בשם גם רומז על ההבדל במשמעות. בכל מקרה פיתרון קל לסיטואציה אחרי שהבנו מה קורה ואם אי אפשר לעבור למשתנים פשוטים הוא להעתיק את האוביקט לפני שמפעילים ref כלומר (בהנחה שאין פונקציות באוביקט):
import { ref } from 'vue'
const init = {count: 0}
const item = ref(JSON.parse(JSON.stringify(init)));

function inc() { item.value.count++ }
function reset() { item.value = init }
או אם אפשר עדיף להחליף את init בפונקציה שמחזירה את הערך הראשוני וככה אתם מבטיחים שאף פעם לא תשנו את הערך הראשוני:
import { ref } from 'vue'
const init = () => ({count: 0})
const item = ref(init());

function inc() { item.value.count++ }
function reset() { item.value = init() }

ToCode
1 418
מבחן ה ack אחד האתגרים בפיתוח תוכנה שאפשר יהיה לתחזק אותה הוא שאנחנו לא יודעים מראש איזה שאלות יעלו בעתיד ואיזה שינויים נצטרך להכניס בקוד. אנחנו גם לא יודעים מי יגיע לקרוא את הקוד, כמה זמן יהיה לו או לה ומה תהיה מטרתם. כתיבת קוד היא מתנה שאנחנו נותנים לעתיד בתנאים של אי וודאות, בלי לדעת מה יהיה אותו עתיד. ולמרות כל אי הוודאות הזאת יש כמה דברים שאפשר לנחש: אפשר לנחש שמי שיגיעו לתחזק את הקוד או לתקן באג ידעו על המערכת הרבה פחות ממה שאתם יודעים עכשיו ושיהיה להם הרבה פחות זמן או מוטיבציה ממה שיש לכם עכשיו. הם רק באו לבדוק או לתקן מנגנון שאמור לעבוד, ויש להם צרות יותר גדולות של העתיד. טריק אחד של התחשבות שאני אוהב ליישם הוא מבחן ה ack, וזה אומר שתהיה לי מילה אחת מרכזית בפיצ'ר שתחזור על עצמה בכל המקומות שהפיצ'ר הזה נוגע בהם וכך אפשר יהיה לקבל תמונת מבט על של הפיצ'ר גם כשמגיעים רק לתקן באג או לבדוק משהו. לדוגמה אם אני בונה פיצ'ר של הפקת תעודות לקורסים אז אני יכול להתחיל עם מילה שקשורה לתעודות כמו certificate ולהמשיך איתה בשמות של מחלקות או פונקציות, וככה נקבל שמות כמו: 1. CertificatesController 2. CertificatesListView 3. isCertificateReady 4. certificateStatus 5. UserInfoForCertificate האנטי תבנית כאן היא להיות יצירתיים ולהשתמש באוצר מילים מגוון מדי, למשל אם אני כותב פונקציה שתבדוק אם משתמש סיים קורס אני לא ארצה לקרוא לה isGraduated (למרות שבמקומות אחרים זה אולי שם טוב) ועדיף לבחור משהו שמכיל את מילת המפתח שלי למשל isCertificateEligible. אל תפחדו להיעזר ב Chat GPT כדי למצוא שמות מתאימים למילת מפתח שבחרתם.

ToCode
1 418
אני עוד אתגעגע ל Facade תבנית העיצוב Facade היא אוביקט שנמצא לפני ספריה אחרת ומסתיר התנהגות מסובכת או מורכבת יותר. המטרה המרכזית של Facade היא לאפשר לנו לכתוב קוד יותר קצר או קריא, או לתת פיתרון ל Use Case מסוים שבו לא צריך את כל המורכבות של ספריה אחרת. לדוגמה בפייתון הספריה requests עוטפת את ממשק התקשורת של השפה ומספקת דרך קלה יותר למשוך מידע מהרשת. יש לה גם יותר פיצ'רים כמובן. ביקשתי מהבוט תוכניות דוגמה עם ובלי requests וזה מה שקיבלתי, תוכנית ראשונה בלי requests רק פייתון:
import urllib.request
import json

def get_data_using_urllib():
    url = "https://api.example.com/data"

    # 1. Create a request object (so you can set headers if you need)
    req = urllib.request.Request(url, headers={"Accept": "application/json"})

    try:
        # 2. Open the URL
        with urllib.request.urlopen(req) as response:
            # 3. Check the status code
            if response.status == 200:
                # 4. Read and parse JSON data
                raw_data = response.read()
                data = json.loads(raw_data)
                print("Data fetched using urllib:", data)
            else:
                print(f"Error: status code = {response.status}")
    except Exception as e:
        print("An error occurred:", e)

if __name__ == "__main__":
    get_data_using_urllib()
תוכנית שניה עם requests:
import requests

def get_data_using_requests():
    url = "https://api.example.com/data"

    try:
        # 1. Make a GET request
        response = requests.get(url)

        # 2. Raise an exception if the status is not 2xx
        response.raise_for_status()

        # 3. Parse JSON in one step
        data = response.json()
        print("Data fetched using Requests:", data)

    except requests.exceptions.RequestException as e:
        print("An error occurred:", e)

if __name__ == "__main__":
    get_data_using_requests()
אחלה פסאדה נכון? אז למה שנתגעגע אליה? אני חושב שזו אשמת אותו בוט בדיוק שיצר את שתי התוכניות. אם פעם ההתלבטות לגבי כתיבת אבסטרקציה חדשה התרכז בעלות של פיתוח אותה אבסטרקציה (תחזוקה, שידרוג כל פעם שהתשתית שהפסאדה בנויה עליה מתעדכנת), בגלל Chat GPT אנחנו מתלבטים עכשיו גם לגבי הערך. כן ברור שעם ספריות קיימות כמו requests אין לו בעיה לכתוב קוד אבל אם אני כותב אבסטרקציה חדשה ה AI עדיין לא מכיר אותה, ולכן לא יוכל לכתוב איתה קוד. עכשיו נסו לדמיין שאתם צריכים להוציא בקשת רשת בפייתון, אופציה אחת היא לקחת את הקוד העובד ש Chat GPT נתן ואופציה שניה היא לשלב ספריה שמבטיחה לעשות את אותו דבר בפחות שורות. הבעיה שכש Chat GPT כותב את הקוד, כמות השורות היא כבר לא פקטור. בראייה קדימה, נדמיין שנתיים קדימה כשכל הקוד שנקבל מ AI יעבוד, יהיה מאוד קשה לשכנע אנשים להשתמש בממשקים "קלים" במקום בממשקים ש Chat GPT יודע לייצר.

ToCode
1 418
ריאקט מול ויו - שימו לב לשינויי סטייט גם בריאקט וגם ב Vue יש תמיכה בערכים מחושבים, אבל הרזולוציה שונה ואנחנו צריכים לשים לב לזה כשכותבים קוד. בריאקט הקומפוננטה הבאה מגדירה שני ערכים ושני ערכים מחושבים:
import "./styles.css";
import { useState } from "react";

export default function App() {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  const twoX = x * 2;
  const twoY = y * 2;

  return (
    <div className="App">
      <label>
        X:
        <input
          type="number"
          value={x}
          onChange={(e) => setX(Number(e.target.value))}
        />
        <span>x = {x}; </span>
        <span>2x = {twoX}</span>
      </label>
      <label>
        Y:
        <input
          type="number"
          value={y}
          onChange={(e) => setY(Number(e.target.value))}
        />
        <span>y = {y}; </span>
        <span>2y = {twoY}</span>
      </label>
    </div>
  );
}
אבל לא היינו כותבים קוד כזה בעולם האמיתי. הרזולוציה לא נכונה. בדוגמת הקוד גם כש x משתנה וגם כש y משתנה יש לחשב מחדש את שני הערכים המחושבים twoX ו twoY, בגלל שרזולוציית שינויים בריאקט היא קומפוננטה. בעולם האמיתי הקומפוננטה הזאת היתה מחולקת לשתי קומפוננטות, גם בגלל כפל הקוד וגם ובעיקר בגלל ה State - לקומפוננטה יש מספר אזורים שכל אחד מהם מושפע באופן עצמאי מסטייט אחר, וזה סימן ברור שמשהו בבחירת הקומפוננטה שלנו לא נכון. ב Vue הסיפור קצת יותר מורכב אבל גם יותר קל למשתמשים. זאת הקומפוננטה:
<script setup>
import {ref, computed} from 'vue';
const x = ref(0);
const y = ref(0);
const twoX = computed(() => x.value * 2);
const twoY = computed(() => y.value * 2);
</script>

<template>
  <div class="App">
      <label>
        X:
        <input
          type="number"
          v-model="x"
        />
        <span>x = {{x}}; </span>
        <span>2x = {{twoX}}</span>
      </label>
      <label>
        Y:
        <input
          type="number"
          v-model="y"
          
        />
        <span>y = {{y}}; </span>
        <span>2y = {{twoY}}</span>
      </label>
    </div>
</template>
הקוד זהה אבל ההתנהגות שונה - שינוי ב x גורם לחישוב מחדש רק של twoX ושינוי ב y גורם לחישוב מחדש רק של twoY, כלומר רזולוציית השינוי היא לפי המשתנה והערכים שמחושבים ממנו. זה אומר שב vue יהיו לנו פחות בעיות ביצועים כתוצאה משינוי שיצא משליטה, אבל מצד שני יותר קשה לנו להבין את ההשפעה האמיתית של כל שינוי כי בשביל לראות מה מושפע מ x צריך לעקוב אחרי ה computed ולהסתכל על ה render function שנוצרה ואיזה חלקים ממנה מושפעים מכל ref. בסוף בשני המקרים יהיה הגיוני לחלק אחרת לקומפוננטות לפי מבנה ה State. אפשר להגיד ש vue יותר מתאמץ בשביל שנוכל "לחיות" עם הטעות בלי לשלם בביצועים וריאקט דורש תשומת לב גבוהה יותר לפרטים בבניית המערכת. שמעתי כבר את הטיעון שבגלל זה קוד ריאקט באופן כללי הוא טוב יותר, כי אנשים יותר מתאמצים לכתוב נכון. אני חייב להודות שמהניסיון שלי הרבה פעמים זה לא ממש עוזר וקוד ריאקט של מערכות גדולות גם לא מחולק נכון לקומפוננטות וכתוצאה מזה גם סובל מבעיות ביצועים.

ToCode
1 418
האם להשתמש בטרנספורמציות על מידע ב echarts? אחד הפיצ'רים החמודים של echarts הוא היכולת להריץ Data Pipeline בתוך קוד יצירת הגרף. אני מודה שאני עדיין מתלבט מתי להשתמש בזה ומתי להריץ את המניפולציה על המידע מראש, במיוחד כשמשלבים את echarts עם ספריית פרונטאנד כמו ריאקט או ויו. בכל מקרה ובינתיים בשביל המשחק בואו נראה איך זה עובד. שימו לב להגדרת המידע הבאה:
  dataset: [
    {
      // 1) Raw data from your JSON array
      id: 'raw',
      source: data
    },
    {
      // 2) Sort descending by SUMACCIDEN
      id: 'sorted',
      fromDatasetId: 'raw',
      transform: {
        type: 'sort',
        config: {
          dimension: 'SUMACCIDEN',  // the field to sort by
          order: 'desc'
        }
      }
    },
  ],
אני מתחיל עם מערך של אוביקטים בשם data, ובמקום למיין אותו מראש בקוד אני משתמש בטרנספורמציה של echarts כדי להסתכל על המידע הממוין. היתרון הוא שאפשר לייצר טרנספורמציות שונות ולהציג גרפים אחרים לכל טרנספורמציה, או לחבר את הטרנספורמציות למערכת ניהול האירועים של echarts. החיסרון הוא שהתיעוד לא מלהיב ואם צריכים טרנספורמציה שאין להם זה יכול לתסכל. לדוגמה לא מצאתי דרך להציג רק 10 ערכים גבוהים ביותר אחרי הסינון. האלטרנטיבה היא להשתמש ב JavaScript פשוט:
const sortedData = data
    .filter(item => item.city && item.SUMACCIDEN)
    .sort((a, b) => b.SUMACCIDEN - a.SUMACCIDEN)
    .slice(0, 10);
ייתרון נוסף של הגישה הפשוטה הוא שלא חייבים לטעון את רכיב הטרנספורמציות, מה שמקטין את הבאנדל. דוגמה? בטח. הקוד הבא יציג לכם את עשר הערים עם הכי הרבה תאונות דרכים בספטמבר 2024 ומספר תאונות הדרכים בכל עיר בגרף עמודות:
<script setup>
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { BarChart } from 'echarts/charts';
import {
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent,
  DatasetComponent,
} from 'echarts/components';
import VChart from 'vue-echarts';
import { ref } from 'vue';

// Register only the components we need (modular import)
use([
  CanvasRenderer,
  BarChart,
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent,
  DatasetComponent,
]);

// Fetch your JSON array of objects (converted from CSV)
const data = await fetch('https://assets.codepen.io/5217/data.json').then(res => res.json());

const sortedData = data
.filter(item => item.city && item.SUMACCIDEN)
.sort((a, b) => b.SUMACCIDEN - a.SUMACCIDEN)
.slice(0, 10);

const option = ref({
  title: {
    text: 'Top 10 Cities with Car Accidents',
    left: 'center'
  },
  tooltip: {
    trigger: 'axis'
  },
  // We use ECharts “dataset” + transforms for sorting and limiting to top 10
  dataset: {
    source: sortedData,
    id: 'sorted'
  },
  xAxis: {
    type: 'category'
  },
  yAxis: {
    name: 'Number of Accidents'
  },
  series: [
    {
      type: 'bar',
      datasetId: 'sorted',
      // Map city -> x-axis category, SUMACCIDEN -> y-axis value
      encode: {
        x: 'city',
        y: 'SUMACCIDEN'
      },
      // Optional styling
      itemStyle: {
        color: '#5470c6'
      }
    }
  ]
});

</script>

<template>
  <!-- v-chart from vue-echarts -->
  <v-chart class="chart" :option="option" autoresize />
</template>

<style scoped>
.chart {
  height: 600px;
  width: 100%;
}
</style>
בשביל להריץ תצטרכו לשים אותו בתוך תוכנית vue ולהתקין את vue-echarts ו echarts.

ToCode
1 418
שלוש סיבות לעבוד מקומית במיוחד בתור מתחילים לא מזמן ראיתי פוסט שהמליץ למתחילים לעבוד רק עם סביבות פיתוח אונליין, כדי לא להסתבך עם התקנות. אני מכיר טוב את הבעיה כולל מחברים שמלמדים תכנות: העבודה מ Google Colab, מ Code Sandbox או סטאקבליץ או אפילו מתוך replit הרבה יותר קלה לתלמידים ומורים מעדיפים לא להסתבך עם התקנות שאולי ייכשלו כי יש בעיה ספציפית למישהו על המחשב. גם התלמידים לא מתלהבים להתקין סביבת פיתוח מקומית. כשלומדים משהו חדש אנחנו להוטים לראות איך זה עובד, ואם אני צריך לשרוף עכשיו חצי יום על התקנות זה יכול להוריד את כל החשק ללימוד. ובכל זאת כדאי לזכור: 1. לא תמיד יהיה לך חיבור אינטרנט - בתור מתחילים (אבל גם בתור וותיקים), יש ערך ליכולת שלנו בכל רגע לפתוח את סביבת הפיתוח ולנסות משהו. ככל שמשהו יותר נגיש אנחנו עושים יותר ממנו, ואם יש לי סביבת פיתוח מותקנת מקומית יש יותר סיכוי שאפעיל אותה ואנסה משהו חדש גם בלי אינטרנט. יותר מזה, באותם מצבים שאין לי אינטרנט חווית הלמידה שלי עשויה להיות טובה יותר כי אני לא אתפתה להעתיק קודים מאיזה AI ויש לי זמן לקרוא ולחשוב על הדברים שאני בונה. 2. זיהוי שגיאות לרוב עובד טוב יותר בסביבה מקומית - נכון ההתקנה הראשונית של סביבה מקומית יותר מורכבת, אבל אחרי שהכל מותקן חווית העבודה השוטפת ובמיוחד הודעות השגיאה נוטות להיות ברורות יותר. אפשר להגיד שזה דבר אחד פחות שיכול להישבר. 3. המחקר קל יותר - בעבודה מקומית יותר קל לנו להיכנס לקוד כולל לקוד של ספריות חיצוניות או הגדרות הפרויקט כדי לראות מה כתוב שם ולהבין מה יש בפנים. היכולת לחקור איך דברים עובדים לרוב עוזרת ללמוד, גם אם היא לפעמים מאטה אותנו. היתה תקופה שחשבתי ש Docker יהיה סוג של פיתרון ביניים, שיאפשר לעבוד עם כלי עבודה חדשים ולדלג על מכשול ההתקנות. אני לא כל כך בטוח בזה יותר: רוב הזמן אפילו כשאני מריץ דברים מתוך דוקר על המחשב שלי ולכאורה לא צריך להתקין כלום יוצא שאני כן צריך להיכנס לקונטיינר ולסדר דברים או להבין איך הם עובדים וזה לא באמת חסך לי זמן או עבודה. מה דעתכם? כשאתם לומדים כלי חדש מעדיפים להתקין מקומית, לפתח אונליין או להריץ קונטיינר?