<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Clarence Gatama Chege</title>
    <description>The latest articles on DEV Community by Clarence Gatama Chege (@clarenceg01).</description>
    <link>https://dev.to/clarenceg01</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1102063%2F99d2ab4b-09cb-4f1d-b6e5-161c9c9343a5.png</url>
      <title>DEV Community: Clarence Gatama Chege</title>
      <link>https://dev.to/clarenceg01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/clarenceg01"/>
    <language>en</language>
    <item>
      <title>JCars Logistics: From Raw Data to Actionable Insights Using Power BI</title>
      <dc:creator>Clarence Gatama Chege</dc:creator>
      <pubDate>Wed, 30 Sep 2026 12:34:08 +0000</pubDate>
      <link>https://dev.to/clarenceg01/jcars-logistics-from-raw-data-to-actionable-insights-using-power-bi-lll</link>
      <guid>https://dev.to/clarenceg01/jcars-logistics-from-raw-data-to-actionable-insights-using-power-bi-lll</guid>
      <description>&lt;p&gt;JCars Logistics is a Kenyan vehicle sales and logistics business operating from a main yard in Nairobi and seven other branches. It sells vehicles to individuals, dealers, corporates, government bodies and NGOs and arranges delivery to customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding the Dataset&lt;/strong&gt;&lt;br&gt;
The raw data came as a single flat CSV export &lt;a href="https://github.com/ClarenceG01/JCars-Logistics-Sales-Performance-Analysis/blob/main/data/Jcars_data.csv" rel="noopener noreferrer"&gt;&lt;code&gt;Jcars_data.csv&lt;/code&gt;&lt;/a&gt; containing 32 columns. The grain of the dataset: a row represents a sale made by Jcar with the following fields recorded for each sale:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Jcars_data.csv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Columns&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;One row per order&lt;/strong&gt; (identified by Order ID)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customer fields&lt;/td&gt;
&lt;td&gt;Name, Type, Age, Rating, Review Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Location fields&lt;/td&gt;
&lt;td&gt;Region, County, City, Branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vehicle fields&lt;/td&gt;
&lt;td&gt;Make, Model, Vehicle Type, Year, Fuel Type, Transmission, Color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money fields&lt;/td&gt;
&lt;td&gt;Unit Selling Price, Unit Cost, Discount, Delivery Fee, Logistics Cost, Revenue Recorded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Process fields&lt;/td&gt;
&lt;td&gt;Order Date, Delivery Date, Payment Method, Payment Status, Delivery Status, Returned, Lead Source, Sales Rep, Unit Sold&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;DATA QUALITY AUDIT&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Missing header&lt;/strong&gt; - header values were saved in first row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing Values&lt;/strong&gt; - all columns except Customer Type, County, City, Branch, Sales Rep, Car make, car model, Fuel Type, Transmission, Color, Unit Selling Price, Payment Method, Payment Status and Revenue Recorded have blank values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent formatting&lt;/strong&gt; - the two date columns , order date and delivery date have different date formats ,for example, 03/20/2026, 10-Feb-25, 19/01/2025. Currency columns also have inconsistent formatting with values having different currencies for example: USD, KES, KSH, ZAR and others not specified. Text columns such as Customer name, Customer Type etc also have formatting issues. Some text values are in proper case, others are fully uppercase and others are fully lowercase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent Categories&lt;/strong&gt; - examples of inconsistent categories in this dataset include &lt;code&gt;government&lt;/code&gt;, &lt;code&gt;Government&lt;/code&gt;, &lt;code&gt;GOVERNMENT&lt;/code&gt;, &lt;code&gt;govt&lt;/code&gt;, &lt;code&gt;GOVT&lt;/code&gt; all representing the same Customer Type. Such cases are also there in other columns like Region, County, City, Branch, Sales Rep, Lead Source, Car Make, Car Model etc&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Possible Outliers&lt;/strong&gt; - in the dataset there is an entry of a customer whose age is 121&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;DATA CLEANING&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1. Headers&lt;/strong&gt;- my first cleaning step was setting the first row as the headers. This can be done in Power Query &lt;code&gt;Transform&lt;/code&gt; Tab under the &lt;code&gt;Table&lt;/code&gt; Subgroup. The screenshot below show the before and after transformation.&lt;/p&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxahp16y1pdjz4re5owdq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxahp16y1pdjz4re5owdq.png" alt="Power bi screenshot showing column names in first row" width="799" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fljfur1zqd08isqfw5rdt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fljfur1zqd08isqfw5rdt.png" alt="Power bi screenshot after first row values were set to headers" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Formatting&lt;/strong&gt; - Most text columns have values in different formats e.g, all uppercase, all lowercase and others in proper case. I converted all text columns to proper case on power query by right clicking each text column and selecting &lt;code&gt;Capitalize Each Word&lt;/code&gt; under Transform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flbmzs2igm33wws8h4fd0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flbmzs2igm33wws8h4fd0.png" alt=" " width="784" height="712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Inconsistent Categories&lt;/strong&gt; - there are columns with different values representing the same thing e.g, &lt;code&gt;government&lt;/code&gt;, &lt;code&gt;Government&lt;/code&gt;, &lt;code&gt;GOVERNMENT&lt;/code&gt;, &lt;code&gt;govt&lt;/code&gt;, &lt;code&gt;GOVT&lt;/code&gt; in &lt;code&gt;Customer Type&lt;/code&gt; column. I used &lt;code&gt;Replace Values&lt;/code&gt; to replace the different values with  a single value, for example for Customer Type government I replaced all values representing government with a common value &lt;code&gt;Government&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwxqxpcc6qc47nogatxal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwxqxpcc6qc47nogatxal.png" alt=" " width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Category columns&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Columns such as Car Make, Region, Branch, Sales Rep, Payment Status and Delivery Status were cleaned with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trim / Clean&lt;/strong&gt; (Transform tab) to remove extra spaces and non-printable characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace Values&lt;/strong&gt;, run once per known misspelling or abbreviation (for example &lt;code&gt;totoya&lt;/code&gt; - &lt;code&gt;Toyota&lt;/code&gt;, &lt;code&gt;nbi&lt;/code&gt; - &lt;code&gt;Nairobi&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;For columns with partial entries (Sales Rep recorded as just &lt;code&gt;Grace&lt;/code&gt; instead of &lt;code&gt;Grace Njeri&lt;/code&gt;), &lt;strong&gt;Replace Values&lt;/strong&gt; was used again with &lt;strong&gt;"Match entire cell contents"&lt;/strong&gt; checked, so the fix only applied to standalone first names and did not corrupt rows that were already correct full names.&lt;/li&gt;
&lt;li&gt;Remaining unmatched or placeholder values (blank, "na", "unknown", "tbd") were replaced with &lt;code&gt;Unknown&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Numeric and currency-style columns&lt;/strong&gt;&lt;br&gt;
Columns Unit Cost, Unit Selling Price, Delivery Fee, Logistics Cost and Revenue Recorded were mixed KES, Ksh, USD, EUR, ZAR, R and an unrecognised ? symbol. Exchange rates used in cleaning were: USD - KES: 129.45,&lt;br&gt;
      EUR - KES: 147.28,&lt;br&gt;
      ZAR - KES: 7.85&lt;br&gt;
Here is how they were standardised to KES:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect currency from the prefix before stripping symbols, using a Conditional Column (Rate Multiplier):
  USD/$/? - 129.45, 
  EUR - 147.28,
  ZAR/R - 7.85,
  KES/KSh/no label - 1.&lt;/li&gt;
&lt;li&gt;The ? symbol did not have a clearly identifiable currency. To determine the most appropriate currency, the affected values were converted using the USD, EUR, and ZAR exchange rates. The USD conversion produced results that were reasonable and fell within the expected range for each row.&lt;/li&gt;
&lt;li&gt;Another Conditional Column was used to flag rows with a M suffix. For these rows the new column stored 1,000,000 as a multiplier, while all other rows were set to 1.&lt;/li&gt;
&lt;li&gt;Replace Values was used to strip the currency labels (USD, EUR, ZAR, R, ?, ,) from the original column, leaving just the raw digits, which was then converted to a numeric type.&lt;/li&gt;
&lt;li&gt;Combine the columns. The three columns (the cleaned numeric value, the Rate Multiplier, and the millions multiplier) were then combined by multiplying them together (product) producing the final standardised value in KES.
** 6. Word-based numbers
Columns like Customer Age (&lt;code&gt;thirty&lt;/code&gt;) and Discount (&lt;code&gt;fifteen&lt;/code&gt;, &lt;code&gt;ten percent&lt;/code&gt;) were handled with &lt;strong&gt;Replace Values&lt;/strong&gt;, mapping each specific word to its digit equivalent, before the column was converted to a numeric type.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;6. Validity ranges&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enforced with &lt;strong&gt;Conditional Column&lt;/strong&gt;, setting any out-of-range value to null:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Valid range&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Customer Age&lt;/td&gt;
&lt;td&gt;18–100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customer Rating&lt;/td&gt;
&lt;td&gt;1–5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discount&lt;/td&gt;
&lt;td&gt;0–50%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;7. Sales Rep specific fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some rep names had a stray &lt;code&gt;1&lt;/code&gt; in place of the letter &lt;code&gt;i&lt;/code&gt; (for example &lt;code&gt;Fa1th&lt;/code&gt; for &lt;code&gt;Faith&lt;/code&gt;). This was corrected with a &lt;strong&gt;Replace Values&lt;/strong&gt; pass (&lt;code&gt;1&lt;/code&gt; to &lt;code&gt;i&lt;/code&gt;) run before the name-matching replacements, and double spaces between first and last names were collapsed with a second Replace Values pass (two spaces - one space).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Derived columns&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Expected Revenue&lt;/code&gt; was added as a &lt;strong&gt;Custom Column&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Units Sold] * [Unit Selling Price] * (1 - [Discount]) + [Delivery Fee]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;9. Date Column Cleaning&lt;/strong&gt;&lt;br&gt;
Dates arrived in mixed formats and some had been stored as whole numbers (Excel serial dates). The steps below were used to clean &lt;code&gt;Order Date&lt;/code&gt; &amp;amp; &lt;code&gt;Delivery Date&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mixed date formats:&lt;/strong&gt; a small number of dates were in &lt;code&gt;mm/dd/yyyy&lt;/code&gt; instead of &lt;code&gt;dd/mm/yyyy&lt;/code&gt;. These were identified and corrected manually using &lt;strong&gt;Replace Values&lt;/strong&gt; to swap them into the consistent &lt;code&gt;dd/mm/yyyy&lt;/code&gt; format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whole-number (serial) dates:&lt;/strong&gt; to isolate these, the date column was &lt;strong&gt;duplicated&lt;/strong&gt;. The duplicate was converted using &lt;strong&gt;Change Type - Whole Number&lt;/strong&gt;, which turned every genuine date-text value into an error/null, leaving only the true serial numbers intact.&lt;/li&gt;
&lt;li&gt;That duplicate was then converted using &lt;strong&gt;Change Type - Date&lt;/strong&gt;, which correctly resolved the remaining serial numbers into proper dates.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Conditional Column&lt;/strong&gt; combined the two: where the original cleaned date column had a value, it was used; where it was null, the converted duplicate (now holding the resolved serial dates) was used instead. This conditional column became the final cleaned date column.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Assumptions and Business Rules
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Grain is one order per row;&lt;/li&gt;
&lt;li&gt;Expected Revenue = Units Sold × Unit Selling Price × (1 − Discount) + Delivery Fee.&lt;/li&gt;
&lt;li&gt;Discounts above 50% are treated as data errors.&lt;/li&gt;
&lt;li&gt;Values without a currency label are assumed to be KES.&lt;/li&gt;
&lt;li&gt;Ambiguous &lt;code&gt;?&lt;/code&gt; currency values were assumed to be USD.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Data Modelling Approach
&lt;/h2&gt;

&lt;p&gt;The flat table was reshaped into a &lt;strong&gt;star schema&lt;/strong&gt;: one fact table of measurable events surrounded by dimension tables of descriptive attributes. Benefits: smaller model (repeated text stored once), faster filtering, and simpler DAX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2karnhyssxjzr886zc5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2karnhyssxjzr886zc5x.png" alt=" " width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  10. Important DAX Measures
&lt;/h1&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total Revenue = SUM(Fact_Sales[Revenue])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Aggregates the pre-computed order revenue; responds to whatever dimension is on the visual.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total Cost = SUMX(Fact_Sales, Fact_Sales[Unit Cost] * Fact_Sales[Units Sold])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total Gross Profit =
SUMX(Fact_Sales, Fact_Sales[Revenue] - (Fact_Sales[Units Sold] * Fact_Sales[Unit Cost]))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight datastudio"&gt;&lt;code&gt;&lt;span class="n"&gt;Gross&lt;/span&gt; &lt;span class="n"&gt;Profit&lt;/span&gt; &lt;span class="n"&gt;Margin&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;DIVIDE&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt; &lt;span class="n"&gt;Gross&lt;/span&gt; &lt;span class="n"&gt;Profit&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt; &lt;span class="n"&gt;Revenue&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Units Sold Total = SUM(Fact_Sales[Units Sold])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Avg Customer Rating = AVERAGE(Fact_Sales[Customer Rating])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total Logistics Cost = SUM(Fact_Sales[Logistics Cost])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Net Logistics Margin = SUM(Fact_Sales[Delivery Fee]) - SUM(Fact_Sales[Logistics Cost])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Calculated column example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Age Band =
SWITCH(TRUE(),
    ISBLANK(Dim_Customer[Customer Age]), "Unknown",
    Dim_Customer[Customer Age] &amp;lt; 25, "18-24",
    Dim_Customer[Customer Age] &amp;lt; 35, "25-34",
    Dim_Customer[Customer Age] &amp;lt; 45, "35-44",
    Dim_Customer[Customer Age] &amp;lt; 55, "45-54",
    "55+")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  11. Executive Dashboard Design
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; monitoring, not investigation.This is to allow stakeholders to quickly understand the organisation’s overall performance at a glance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F20fc9ap93lzbg1xld1pc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F20fc9ap93lzbg1xld1pc.png" alt=" " width="768" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Detailed Report Structure
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fowqkysfr5jmappt2aoul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fowqkysfr5jmappt2aoul.png" alt=" " width="761" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl8xt6vgp0oz0fdjpdpix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl8xt6vgp0oz0fdjpdpix.png" alt=" " width="762" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ze5xiacwtilxtyddgd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ze5xiacwtilxtyddgd6.png" alt=" " width="765" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmrb3t4ggjlqy2g8fqta.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmrb3t4ggjlqy2g8fqta.png" alt=" " width="768" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoxkw458vnrz2c4j8d7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoxkw458vnrz2c4j8d7r.png" alt=" " width="760" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frpwxihzni0exoewbkrfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frpwxihzni0exoewbkrfe.png" alt=" " width="758" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Key Findings
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Kakamega sold the most units, collected the highest revenue, and had the most fully paid units.&lt;/li&gt;
&lt;li&gt;Thika had the most refunded units.&lt;/li&gt;
&lt;li&gt;Only Athi River, Kakamega, Mombasa and Nakuru had a positive gross profit margin.&lt;/li&gt;
&lt;li&gt;Sales were heavily dominated by Toyota (144 units), with Mazda second at 69 units, less than half.&lt;/li&gt;
&lt;li&gt;Toyota had the highest gross profit (KSh 23.26M); Isuzu had the largest gross loss (KSh -23.06M).&lt;/li&gt;
&lt;li&gt;Peter Kiptoo sold the most units of the most profitable vehicle.&lt;/li&gt;
&lt;li&gt;Mitsubishi, Mercedes, Mazda, Isuzu and BMW had more than 50% of units not fully paid.&lt;/li&gt;
&lt;li&gt;Orders with the most cancelled payments were mainly handled by Daniel Kimani, Grace Njeri and Faith Achieng.&lt;/li&gt;
&lt;li&gt;Kakamega and Thika had the highest delivery fees and logistics costs.&lt;/li&gt;
&lt;li&gt;Delivery fees are KSh 2.99M below logistics costs.&lt;/li&gt;
&lt;li&gt;Customer view: the 55+ age band generated the highest revenue (KSh 0.36bn); the top customer was John Kariuki (KSh 53M); Individual customers had the highest share of fully paid units. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume does not equal profit.&lt;/strong&gt; Most branches sell at a negative margin, so growth without margin discipline increases losses.&lt;/li&gt;
&lt;li&gt;A large share of units across five makes is not fully paid, creating collection risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery is a cost centre.&lt;/strong&gt; Fees recover less than logistics spend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The best branch is also expensive to serve.&lt;/strong&gt; Kakamega leads on sales and collection but also on logistics cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Recommendations
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tighten payment collection&lt;/strong&gt; — set up minimum deposits for each order and do payment  follow-ups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review Isuzu and loss-making branches&lt;/strong&gt; — check pricing and total cost incurred.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reprice delivery&lt;/strong&gt; either based on distance or vehicle type to close the KSh 2.99M logistics gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Investigate Thika's high return rate&lt;/strong&gt; and improve pre-delivery checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coach sales reps&lt;/strong&gt; on cancellation-heavy accounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replicate Kakamega's sales practices&lt;/strong&gt; elsewhere while monitoring its cost-to-serve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improve data capture&lt;/strong&gt; — mandatory cost fields, single currency at entry and age range validation.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  20. Conclusion
&lt;/h1&gt;

&lt;p&gt;The original flat file contained inconsistencies that needed to be addressed before meaningful analysis could be performed. After cleaning and validating the data, it was transformed into a structured star schema with reusable DAX measures and an interactive dashboard. The analysis shows that JCars Logistics generates strong sales but faces challenges from low margins across many branches, a significant share of unpaid units, and delivery costs that are not fully covered by delivery fees. Future analysis can be strengthened through better data collection, including accurate unit costs, reliable customer identifiers and consistent currency records.&lt;/p&gt;

&lt;p&gt;The full project, including the Power BI file and supporting files are available on GitHub: &lt;strong&gt;&lt;a href="https://github.com/ClarenceG01/JCars-Logistics-Sales-Performance-Analysis" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>data</category>
      <category>database</category>
    </item>
    <item>
      <title>Data Modeling, Relationships, and Joins in Power BI: A Practical Guide</title>
      <dc:creator>Clarence Gatama Chege</dc:creator>
      <pubDate>Mon, 14 Sep 2026 18:56:15 +0000</pubDate>
      <link>https://dev.to/clarenceg01/data-modeling-relationships-and-joins-in-power-bi-a-practical-guide-1bbi</link>
      <guid>https://dev.to/clarenceg01/data-modeling-relationships-and-joins-in-power-bi-a-practical-guide-1bbi</guid>
      <description>&lt;p&gt;Power BI is a business intelligence tool that allows you to connect to various data sources, visualize the data in reports and dashboards, and then share them. Power BI helps to track Key Performance Indicators (KPIs) and helps businesses to get insights that inform decision making.&lt;/p&gt;

&lt;p&gt;This article explores data modeling in Power BI, comparing flat table, star schema, and snowflake schema designs; the role of fact and dimension tables; how relationships and filter direction work within the model; how joins in Power Query differ from relationships in the data model; and finally, a recommended approach for structuring a typical business intelligence solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Modeling
&lt;/h2&gt;

&lt;p&gt;Data modeling is the process of creating a structured and visual representation of data and their relationships. Without data modeling, Power BI projects suffer from bloated file sizes, slow performance, and incorrect report numbers.&lt;/p&gt;

&lt;p&gt;A well-designed data model is important for several reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reporting and analytics&lt;/strong&gt; - a clean data model is crucial because it ensures fast report performance, accurate calculations, and a simplified structure that allows users to easily build and scale their own analytics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DAX calculations&lt;/strong&gt; - DAX measures rely on relationships between tables to filter and aggregate data correctly. A strong model keeps DAX formulas simple and predictable; a weak one forces overly complex workarounds or produces incorrect results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt; - a clean data model reduces memory usage, allowing dashboards and interactive visuals to load and filter instantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt; - a good model can absorb new tables and new data sources without requiring a complete redesign.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt; - when tables are logically separated with clear relationships, the model is easier for others to understand, troubleshoot and use over time.&lt;/p&gt;

&lt;p&gt;In summary, a well-designed model makes reporting, analysis, calculations and performance much easier, while a poorly designed one creates unnecessary challenges at every stage.&lt;/p&gt;

&lt;p&gt;There are primarily three structural types of data modeling in Power BI: Flat Table, Star Schema and Snowflake Schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Flat Table&lt;/strong&gt;&lt;br&gt;
A flat table is a single, wide table that stores all relevant information in one place. It has no relationships and the data is in a single, spreadsheet-like structure. This is typically what a dataset looks like the moment it's loaded into Power BI from a single Excel file or CSV before any modeling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3lmkb7iro06scc2qus6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3lmkb7iro06scc2qus6g.png" alt=" " width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Star Schema&lt;/strong&gt;&lt;br&gt;
A star schema is a data modeling approach where a single central fact table is connected directly to multiple surrounding dimension tables, forming a shape that resembles a star when viewed in Power BI's Model view. It is the most widely recommended structure for Power BI reporting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fact table&lt;/strong&gt; - a central table in a data model that stores the measurable, numeric events of the business along with foreign keys linking each row to the relevant dimensions. &lt;br&gt;
&lt;strong&gt;Dimension table&lt;/strong&gt; - a supporting table that stores descriptive attributes about the entities involved in the events stored in fact tables. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrlharzed1as3ypfbtf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrlharzed1as3ypfbtf9.png" alt=" " width="799" height="420"&gt;&lt;/a&gt;&lt;br&gt;
The screenshot above shows a Star Schema. &lt;code&gt;FactSales&lt;/code&gt; sits at the centre of the model, connected by four separate relationship lines to &lt;code&gt;DimCustomer&lt;/code&gt;, &lt;code&gt;DimProduct&lt;/code&gt;, &lt;code&gt;DimLocation&lt;/code&gt; and &lt;code&gt;DimDate&lt;/code&gt; with their relationships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages of Star Schema&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple, intuitive relationships&lt;/strong&gt; - every dimension links directly to the fact table, making the model easy to read, navigate and explain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplifies DAX&lt;/strong&gt; — Dimension and fact responsibilities are separated, making measure logic clear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to build reports&lt;/strong&gt; - dragging fields from dimension tables onto a visual filters the fact table correctly with no extra configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highly scalable&lt;/strong&gt; - Supports adding new dimensions without affecting existing structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages of Star Schema&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Redundancy&lt;/strong&gt; - Denormalized dimension tables repeat attribute values across multiple rows, which can increase storage needs. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Risk of Inconsistency&lt;/strong&gt; - Duplicated data means that updating an attribute (like renaming a product category) may require changes in multiple rows, risking data quality issues if not done correctly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where it is appropriate&lt;/strong&gt;&lt;br&gt;
The star schema is the default, recommended structure for most business intelligence projects. It is particularly appropriate when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building standard reporting solutions such as sales analysis, financial reporting, or operational dashboards.&lt;/li&gt;
&lt;li&gt;Dimension tables are moderate in size and don't require complex, multi-level hierarchies to be broken out separately.&lt;/li&gt;
&lt;li&gt;Report performance and simplicity of DAX are priorities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implications for Power BI performance and complexity:&lt;/strong&gt;&lt;br&gt;
A star schema significantly boosts Power BI performance by allowing the VertiPaq engine to optimize data compression and accelerate query execution through direct relationship paths. Also the star schema minimizes data complexity by separating numeric facts from descriptive attributes, streamlining DAX calculations and simplifying the overall reporting model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Snowflake Schema&lt;/strong&gt;&lt;br&gt;
A snowflake schema is an extension of the star schema in which one or more dimension tables are further broken down into related sub dimension tables, normalizing the data further and producing a shape that resembles a snowflake when viewed in Power BI's Model view.&lt;/p&gt;

&lt;p&gt;In a snowflake schema, a dimension table that would normally sit as a single table in a star schema is split into two or more related sub dimension tables, each holding a narrower set of attributes. The lowest level dimension retains a direct relationship to the fact table, while the additional sub dimension tables branch outward from it in a chain, with each table connected to the next through its own primary and foreign key. This continues until the attributes have been separated into as many normalized levels as the hierarchy requires, resulting in a structure where the fact table sits at the centre, immediate dimensions surround it as in a star schema, and further related tables extend outward from those dimensions rather than connecting directly back to the fact table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F62nidjjiakqia98ct1v7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F62nidjjiakqia98ct1v7.png" alt=" " width="799" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshot shows the snowflake schema in Model view, with FactSales connected directly to DimProduct as in the star schema, but DimProduct_Snowflake now extending further to DimSubcategory and then to DimCategory, illustrating the extra normalization and relationship hops that distinguish a snowflake schema from a star schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages of Snowflake schema&lt;/strong&gt;&lt;br&gt;
A snowflake schema reduces data redundancy further than a star schema, since repeated text values such as category names are stored once in their own table rather than repeated across every product row. It can also save storage space when a dimension table is very large and its higher level attributes repeat extensively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages of Snowflake schema&lt;/strong&gt;&lt;br&gt;
The additional tables mean more relationships and more relationship hops for filters to travel through, which increases model complexity. DAX measures can become harder to write and slower to evaluate when a filter has to pass through several linked dimension tables before reaching the fact table. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it is appropriate&lt;/strong&gt;&lt;br&gt;
A snowflake schema is appropriate when a dimension table is very large and highly repetitive at higher levels, when strict normalization is required for governance or auditing reasons, or when a dimension naturally has a deep, well-defined hierarchy that benefits from being modeled as separate levels, such as Category, Subcategory, and Product. It is generally used on specific dimensions rather than applied across an entire model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implications for Power BI performance and complexity&lt;/strong&gt;&lt;br&gt;
Because filters must pass through additional relationship hops before reaching the fact table, snowflake schemas are slower than star schemas and place more load on the DAX engine when aggregating across levels. Model complexity is also higher, since there are more tables and relationships to maintain, making the snowflake schema best reserved for specific dimensions that genuinely benefit from normalization rather than the whole model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relationships in Power BI&lt;/strong&gt;&lt;br&gt;
Relationships in Power BI refer to the logical connections between two or more tables based on a common column. These connections enable Power BI to combine data from multiple tables and perform calculations across them, just like joins in traditional databases.&lt;/p&gt;

&lt;p&gt;Relationships are the core of any data model because star and snowflake schemas intentionally divide information into separate tables. Without these connections, the tables would stay completely isolated, preventing data from being filtered, combined, or analyzed together.&lt;/p&gt;

&lt;p&gt;Every relationship in Power BI has a cardinality which describes how many matching rows can exist on each side of the relationship.&lt;br&gt;
&lt;strong&gt;1. One-to-Many (1:*)&lt;/strong&gt;&lt;br&gt;
One record in the first table is related to multiple records in the second table.&lt;/p&gt;

&lt;p&gt;For example, in our model, DimCustomer has one row per customer, while FactSales can contain many rows for the same customer, since a customer can place multiple orders. CustomerID is unique in DimCustomer, but repeats in FactSales wherever that customer appears again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu3pae2n3z5vuu0o1ms04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu3pae2n3z5vuu0o1ms04.png" alt=" " width="548" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. One-to-One (1:1)&lt;/strong&gt;&lt;br&gt;
Each row in the first table relates to exactly one row in the second table, and vice versa, meaning the key column is unique on both sides of the relationship.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7fziej4kdfpxdtwute39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7fziej4kdfpxdtwute39.png" alt=" " width="475" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Many to Many (:)&lt;/strong&gt;&lt;br&gt;
Rows in both tables can have multiple matching rows on the other side, meaning the key column is not unique in either table.&lt;/p&gt;

&lt;p&gt;For example, a student can enroll in many courses and each course can have many students. Neither StudentID nor CourseID is unique on its own, since the same student appears against multiple courses and the same course appears against multiple students.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primary Keys and Foreign Keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A primary key&lt;/strong&gt; is the column in a table that uniquely identifies each row, such as CustomerID in DimCustomer. &lt;br&gt;
&lt;strong&gt;A foreign key&lt;/strong&gt; is a column in another table that references that primary key, such as CustomerID appearing in FactSales. This is why CustomerID contains unique values in DimCustomer, appearing once per customer, but appears multiple times in FactSales as a foreign key, once for every order that customer places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unique Keys, Cardinality and Referential Integrity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unique Keys&lt;/strong&gt;
For a relationship to work correctly in Power BI, the column on the “one” side must contain unique values. A unique key allows Power BI to establish a valid relationship between tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cardinality&lt;/strong&gt;
Cardinality defines the type of relationship created between the tables based on how their key values correspond.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Referential integrity&lt;/strong&gt;
Referential integrity means that every foreign key in one table has a corresponding primary key in the related table. For instance, each CustomerID in FactSales should have a matching CustomerID in DimCustomer. If some foreign key values do not have matching records, Power BI may produce blank or unexpected results in reports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Active and Inactive Relationships&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Active and inactive relationships become important when tables can be connected through multiple relationship paths. Power BI permits only one active relationship between two tables at a time and this relationship is automatically used when applying filters and performing calculations. Other possible relationships are treated as inactive and must be specifically enabled within a DAX measure using functions such as USERELATIONSHIP.&lt;/p&gt;

&lt;p&gt;For example, suppose the FactSales table contains both OrderDate and ShipDate, with each column connected to the DimDate table. Only one of these relationships can remain active at a time. The other relationship is inactive and can be temporarily activated in a measure when analysis needs to be based on the shipping date rather than the order date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter Direction&lt;/strong&gt;&lt;br&gt;
Filter direction determines how filtering behaves once a relationship exists, specifically which table's filters are allowed to affect the other table.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single-Direction Filtering&lt;/strong&gt;&lt;br&gt;
In single-direction filtering, filters flow only one way, typically from the "one" side of a relationship to the "many" side. This is the default and most common setting in Power BI. For example, if you select a product from a DimProduct slicer, Power BI filters the FactSales table so that it displays only sales related to that selected product. However, the filtering does not work in reverse, changes or selections in FactSales will not filter the DimProduct table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bi-Direction Filtering&lt;/strong&gt;&lt;br&gt;
In bidirectional filtering, filters flow both ways, meaning a selection in either table can affect the other. For example, if bidirectional filtering were enabled between DimProduct and FactSales, filtering FactSales down to a specific store location would also filter DimProduct, showing only the products that were sold at that location, in addition to the usual DimProduct-to-FactSales filtering. This is different from single-direction filtering, where FactSales selections have no effect on DimProduct at all.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Joins in Power Query&lt;/strong&gt;&lt;br&gt;
A join, in the context of Power Query, is a way of combining two tables based on a matching column between them, using the Merge Queries feature. Unlike a relationship in the data model, a merge in Power Query happens during data preparation, before the model is even loaded, and it produces a new, physically combined table rather than simply linking two existing ones.&lt;/p&gt;

&lt;p&gt;Throughout this section, we'll use two sample tables:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customers&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CustomerID&lt;/th&gt;
&lt;th&gt;CustomerName&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Orders&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OrderID&lt;/th&gt;
&lt;th&gt;CustomerID&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  1. Left Outer Join
&lt;/h3&gt;

&lt;p&gt;Returns &lt;strong&gt;all rows from the left (first) table&lt;/strong&gt;, plus matching rows from the right (second) table. Where there's no match, the right table's columns are filled with &lt;code&gt;null&lt;/code&gt;.&lt;br&gt;
Every row from the left table, matched or not, is retained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Merge &lt;code&gt;Customers&lt;/code&gt; (left) with &lt;code&gt;Orders&lt;/code&gt; (right) on &lt;code&gt;CustomerID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CustomerID&lt;/th&gt;
&lt;th&gt;CustomerName&lt;/th&gt;
&lt;th&gt;OrderID&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is the &lt;strong&gt;default and most common join&lt;/strong&gt; in Power Query — used when you want to enrich a primary table (e.g., Customers, Products) with related data without losing any of its original rows.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Right Outer Join
&lt;/h3&gt;

&lt;p&gt;Returns &lt;strong&gt;all rows from the right (second) table&lt;/strong&gt;, plus matching rows from the left table. Unmatched left-table columns are &lt;code&gt;null&lt;/code&gt;.&lt;br&gt;
Every row from the right table, matched or not, is retained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Merge &lt;code&gt;Customers&lt;/code&gt; (left) with &lt;code&gt;Orders&lt;/code&gt; (right) on &lt;code&gt;CustomerID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CustomerID&lt;/th&gt;
&lt;th&gt;CustomerName&lt;/th&gt;
&lt;th&gt;OrderID&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Full Outer Join
&lt;/h3&gt;

&lt;p&gt;Returns &lt;strong&gt;all rows from both tables&lt;/strong&gt;, matched where possible, with &lt;code&gt;null&lt;/code&gt; filled in wherever a match doesn't exist on either side.&lt;br&gt;
Every row from both tables is retained. Nothing is dropped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Merge &lt;code&gt;Customers&lt;/code&gt; and &lt;code&gt;Orders&lt;/code&gt; on &lt;code&gt;CustomerID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CustomerID&lt;/th&gt;
&lt;th&gt;CustomerName&lt;/th&gt;
&lt;th&gt;OrderID&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Inner Join
&lt;/h3&gt;

&lt;p&gt;Returns &lt;strong&gt;only the rows where the key exists in both tables&lt;/strong&gt;.&lt;br&gt;
Only matched rows are retained. Anything unmatched on either side is dropped entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Merge &lt;code&gt;Customers&lt;/code&gt; and &lt;code&gt;Orders&lt;/code&gt; on &lt;code&gt;CustomerID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CustomerID&lt;/th&gt;
&lt;th&gt;CustomerName&lt;/th&gt;
&lt;th&gt;OrderID&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  5. Left Anti Join
&lt;/h3&gt;

&lt;p&gt;Returns &lt;strong&gt;only the rows from the left table that have NO match&lt;/strong&gt; in the right table. It is the logical opposite of an Inner Join, restricted to the left side.&lt;/p&gt;

&lt;p&gt;Left-table rows with no corresponding right-table match are retained. No columns from the right table are brought in (there's nothing to bring in, since by definition nothing matched).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Merge &lt;code&gt;Customers&lt;/code&gt; (left) with &lt;code&gt;Orders&lt;/code&gt; (right) on &lt;code&gt;CustomerID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CustomerID&lt;/th&gt;
&lt;th&gt;CustomerName&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  6. Right Anti Join
&lt;/h3&gt;

&lt;p&gt;Returns &lt;strong&gt;only the rows from the right table that have NO match&lt;/strong&gt; in the left table.&lt;br&gt;
Right-table rows with no corresponding left-table match are retained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Merge &lt;code&gt;Customers&lt;/code&gt; (left) with &lt;code&gt;Orders&lt;/code&gt; (right) on &lt;code&gt;CustomerID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OrderID&lt;/th&gt;
&lt;th&gt;CustomerID&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Power Query Joins vs Power BI Relationships&lt;/strong&gt;&lt;br&gt;
A Power Query merge combines data by adding columns from one table to another before the data is loaded into Power BI, resulting in one combined table. In contrast, a Power BI relationship keeps the tables separate and defines how filters should move between them when queries and reports are processed. &lt;/p&gt;

&lt;p&gt;A merge is useful when you need a combined table for export or when you want to permanently add a small number of lookup columns. A relationship is more suitable when the tables represent different entities that need to be analyzed independently in reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended Power BI Model&lt;/strong&gt;&lt;br&gt;
For most business intelligence projects, a star schema is a strong choice. It typically consists of a central fact table, such as FactSales, linked to shared dimension tables such as DimCustomer, DimProduct, DimDate, and DimLocation. These tables are usually connected using one to many relationships, with filters flowing in a single direction from the dimension tables to the fact table.&lt;/p&gt;

&lt;p&gt;Compared with a flat table, a star schema reduces the repetition of descriptive information across large numbers of fact records. This helps keep the model more compact and can improve refresh performance. It is also generally simpler than a snowflake schema because it reduces the number of relationships and filtering steps required by the model. This makes DAX calculations easier to write and the overall model easier to understand.&lt;/p&gt;

&lt;p&gt;A star schema also provides several practical benefits. It supports consistent and efficient filtering, works well with common DAX and time based calculations, and gives report developers a clear structure that is easy to navigate. Since descriptive attributes are maintained in dimension tables rather than repeatedly stored in the fact table, unnecessary data duplication is reduced. The model can also grow more easily as the amount of business data increases, while changes to business information can usually be made in one central location.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
In conclusion, effective Power BI modelling depends on choosing the appropriate schema, separating fact and dimension tables, and establishing well-designed relationships between them. A star schema is generally preferred because it reduces data duplication, improves query performance, simplifies DAX calculations and makes the model easier to understand and maintain. Using reliable keys and controlled filter directions also helps ensure accurate and predictable results, while Power Query merges should be reserved for situations where combining data during preparation is necessary. Overall, applying these principles creates a Power BI model that is efficient, scalable, easy to manage, and suitable for reliable business reporting and analysis.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building an Interactive Excel Dashboard for E-commerce Product Analysis: A Case Study of Jumia Products</title>
      <dc:creator>Clarence Gatama Chege</dc:creator>
      <pubDate>Sat, 05 Sep 2026 22:06:19 +0000</pubDate>
      <link>https://dev.to/clarenceg01/building-an-interactive-excel-dashboard-for-e-commerce-product-analysis-a-case-study-of-jumia-1cod</link>
      <guid>https://dev.to/clarenceg01/building-an-interactive-excel-dashboard-for-e-commerce-product-analysis-a-case-study-of-jumia-1cod</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this project, I used a Jumia product dataset to demonstrate how Excel can be used to explore, prepare, and clean data then use the data for analysis to get insights.&lt;/p&gt;

&lt;p&gt;A final workbook, available in this GitHub repo, is organized into separate sheets for the raw data, cleaned data, analysis, PivotTables, dashboard, and data dictionary. The original data is preserved in the Raw_Data sheet, while all cleaning activities are performed in the Cleaned_Data sheet.&lt;/p&gt;

&lt;p&gt;Our initial raw dataset has 5 columns and 116 rows. The table below represents each column:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source Field&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Expected Cleaned Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Product&lt;/td&gt;
&lt;td&gt;Product name&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current Price&lt;/td&gt;
&lt;td&gt;Current selling price in Kenyan shillings (KSh)&lt;/td&gt;
&lt;td&gt;Number / Currency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old Price&lt;/td&gt;
&lt;td&gt;Price before discount in KSh&lt;/td&gt;
&lt;td&gt;Number / Currency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discount&lt;/td&gt;
&lt;td&gt;Advertised percentage discount&lt;/td&gt;
&lt;td&gt;Percentage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review&lt;/td&gt;
&lt;td&gt;Number of customer reviews&lt;/td&gt;
&lt;td&gt;Whole Number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ratingd&lt;/td&gt;
&lt;td&gt;Average rating out of 5&lt;/td&gt;
&lt;td&gt;Decimal Number&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Initial Data-Quality Audit
&lt;/h2&gt;

&lt;p&gt;Before cleaning, I reviewed the raw dataset to identify issues that would affect analysis. This is what I uncovered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Column headers&lt;/strong&gt;: one misspelled header (Ratingd).&lt;br&gt;
&lt;strong&gt;Data types&lt;/strong&gt;: Current Price and Old Price stored as text. Discount stored as plain text rather than a percentage.&lt;br&gt;
&lt;strong&gt;Duplicates&lt;/strong&gt;: 3 duplicate entries.&lt;br&gt;
&lt;strong&gt;Missing values&lt;/strong&gt;: columns &lt;code&gt;Review&lt;/code&gt; and &lt;code&gt;Ratingd&lt;/code&gt;. Here is a summary of missing values for each column using &lt;code&gt;COUNTBLANK&lt;/code&gt; formula&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fznxevhhjxb1trjqyk1jg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fznxevhhjxb1trjqyk1jg.png" alt=" " width="361" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inconsistent formatting&lt;/strong&gt;: Headers &lt;code&gt;Current price&lt;/code&gt; and &lt;code&gt;old price&lt;/code&gt; are written in different cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgtzd1knutj4do2b7y9j4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgtzd1knutj4do2b7y9j4.png" alt=" " width="308" height="69"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Cleaning
&lt;/h2&gt;

&lt;p&gt;Before I dived into cleaning the dataset, I created a new sheet named &lt;code&gt;cleaned-data&lt;/code&gt; and copied the dataset. This new sheet is where all the cleaning happened.&lt;br&gt;
I also formatted my dataset header by adding a background and bolding the text and used &lt;code&gt;Freeze Panes&lt;/code&gt; to ensure the header is always at the top even while scrolling for easier column identification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Removing Duplicates&lt;/strong&gt;&lt;br&gt;
A record is considered a duplicate only when all of its column values are identical.&lt;/p&gt;

&lt;p&gt;To remove duplicates, I selected a cell within the dataset and went to the Data tab. Under the Data Tools group, I selected Remove Duplicates. Excel displayed a dialog box listing all the columns in the dataset. I left all columns selected and clicked OK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fseyaqmxcbajgphz3dxxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fseyaqmxcbajgphz3dxxp.png" alt="Remove duplicates" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Excel found 3 duplicate rows and removed them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Cleaning the Rating Column&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next I moved on to cleaning individual columns, starting with the Ratingd column. The first issue I noticed was that the column name was misspelled as &lt;code&gt;Ratingd&lt;/code&gt; and I changed it to &lt;code&gt;Rating&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The values in this column were also stored in a format such as &lt;code&gt;4.5 out of 5&lt;/code&gt;, it would be better if we had a numeric value for analysis for example &lt;code&gt;4.5&lt;/code&gt;. I used Find &amp;amp; Replace to remove the text &lt;code&gt;out of 5&lt;/code&gt; from the values, leaving only the numeric rating.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpx75vk3xrc1u4vr74f5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpx75vk3xrc1u4vr74f5.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After removing the text &lt;code&gt;out of 5&lt;/code&gt;, I changed the column type to &lt;code&gt;Number&lt;/code&gt; and set the values to one decimal place.&lt;/p&gt;

&lt;p&gt;Applying filtering on Rating column helped me identify missing values (blanks). Before deciding how to handle the missing values, I checked other columns to look for a pattern behind the blanks. I noted &lt;strong&gt;&lt;code&gt;Review&lt;/code&gt;&lt;/strong&gt; column also had blanks and all rows with a blank in Review column also had a blank in Rating column. This led me to the conclusion that the reason for the blanks in Rating column is because the products were not reviewed. I decided to replace the blanks with text &lt;code&gt;Not Rated&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqagplnt6uc319hn6cph3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqagplnt6uc319hn6cph3.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cleaning the Review Column&lt;/strong&gt;&lt;br&gt;
While cleaning &lt;code&gt;Rating&lt;/code&gt; column I identified that there are products that did not get reviewed and hence had blanks in rating. I replaced the blanks, in &lt;code&gt;Review&lt;/code&gt; column with number &lt;code&gt;0&lt;/code&gt;. This shows that products with &lt;code&gt;0&lt;/code&gt; reviews were &lt;code&gt;Not Rated&lt;/code&gt; supporting &lt;code&gt;Rating&lt;/code&gt; column cleaning decision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn18mlqtjz7zcsj27f5x5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn18mlqtjz7zcsj27f5x5.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also noted that the rest of the values in &lt;code&gt;Review&lt;/code&gt; column had negative values yet the column represents the number of customer reviews. I converted the values to positive numbers using the Absolute function &lt;code&gt;Abs(value)&lt;/code&gt; using the following step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a new column with same name &lt;code&gt;Review&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On first row, set formula &lt;code&gt;Abs(E2)&lt;/code&gt; E2 referencing the first value on original &lt;code&gt;Review&lt;/code&gt; column.&lt;/li&gt;
&lt;li&gt;Double-clicking the fill handle to apply the formula to all rows.&lt;/li&gt;
&lt;li&gt;Copy the new &lt;code&gt;Review&lt;/code&gt; column.&lt;/li&gt;
&lt;li&gt;On the same column, I pasted the values&lt;/li&gt;
&lt;li&gt;Delete the original &lt;code&gt;Review&lt;/code&gt; column&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Cleaning the Current Price &amp;amp; Old Price Column&lt;/strong&gt;&lt;br&gt;
Both columns &lt;code&gt;Current Price&lt;/code&gt; and &lt;code&gt;Old Price&lt;/code&gt; values are aligned to the left, meaning they are stored as text. To record them as numeric values I selected both columns and used &lt;strong&gt;Find &amp;amp; Replace&lt;/strong&gt; to replace &lt;code&gt;Ksh&lt;/code&gt; with blank. Most values immediately shifted and were aligned to the right.&lt;/p&gt;

&lt;p&gt;A hack to identify any non-numeric values in a numeric column is sorting the columns from largest to smallest. Any present text values appear at the top. This helped me discover a product whose &lt;code&gt;Old Price&lt;/code&gt; and &lt;code&gt;Current Price&lt;/code&gt; were recorded as a range. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F889nzh1v9iy42j75w0zb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F889nzh1v9iy42j75w0zb.png" alt=" " width="799" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;Old Price&lt;/code&gt; of the product, I used the middle value of the range which is &lt;code&gt;2,700&lt;/code&gt;. I then used the discount percentage provided in the dataset to calculate the corresponding &lt;code&gt;Current Price&lt;/code&gt; which was &lt;code&gt;1,674&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After cleaning the data, I converted the cleaned range into an Excel Table using Ctrl + T. I confirmed that the table had headers and gave it the name &lt;code&gt;tblProducts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxxk3sldyxpo3obsnniik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxxk3sldyxpo3obsnniik.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Excel Formulas and Enrichment Fields
&lt;/h2&gt;

&lt;p&gt;After cleaning the individual columns, I used Excel formulas to create additional fields that provide more useful information for analysis. These derived fields are calculated from the existing columns rather than being directly provided in the original dataset.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;New Field&lt;/th&gt;
&lt;th&gt;Based On&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Categories&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Price Category&lt;/td&gt;
&lt;td&gt;Current Price&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;Low Price, Middle Price &amp;amp; High Price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discounted Amount&lt;/td&gt;
&lt;td&gt;Current Price &amp;amp; Old Price&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;Difference between Old Price and Current Price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discount Category&lt;/td&gt;
&lt;td&gt;Discount&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;Low Discount, Middle Discount &amp;amp; High Discount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating Category&lt;/td&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;Missing, Poor, Average &amp;amp; Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engagement&lt;/td&gt;
&lt;td&gt;Review&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;Missing, Strong Engagement &amp;amp; Normal Engagement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Derived Fields, Formulas and Thresholds
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Price Category&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Formula:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;=IF([@[Current Price]]="","Missing",IF([@[Current Price]]&amp;lt;=Price_Q1,"Low Price",IF([@[Current Price]]&amp;lt;=Price_Q3,"Medium Price","High Price")))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note that Price_Q1 and Price_Q3 are named cells representing Current Price Column First Quartile and Third Quartile respectively.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;=QUARTILE.INC(tblProducts[Current Price],1)&lt;/code&gt;&lt;br&gt;
&lt;code&gt;=QUARTILE.INC(tblProducts[Current Price],3)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Discounted Amount&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Formula:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;=[@[Old Price]]-[@[Current Price]]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This calculates the difference between the original price and the current selling price.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Discount Category&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Formula:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;=IF([@Discount]="","Missing",IF([@Discount]&amp;lt;20%,"Low Discount",IF([@Discount]&amp;lt;=40%,"Medium Discount","High Discount")))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Thresholds:&lt;/p&gt;

&lt;p&gt;Blank - Missing&lt;br&gt;
Below 20% - Low Discount&lt;br&gt;
20%–40% - Middle Discount&lt;br&gt;
Above 40% - High Discount&lt;br&gt;
&lt;strong&gt;4. Rating Category&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Formula:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;=IF([@Rating]="","Missing",IF([@Rating]&amp;lt;3,"Poor",IF([@Rating]&amp;lt;=4.5,"Average","Excellent")))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Thresholds:&lt;/p&gt;

&lt;p&gt;Blank - Missing&lt;br&gt;
Below 3 - Poor&lt;br&gt;
3.1–4.4 - Average&lt;br&gt;
4–5-5 - Excellent&lt;br&gt;
&lt;strong&gt;5. Engagement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Formula:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;=IF([@Review]="","Missing",IF([@Review]&amp;gt;=Review_Q3,"Strong Engagement","Normal Engagement"))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note that Review_Q3 is a named cell representing the third quartile of the Review column. It is used as the threshold for identifying products with strong customer engagement.&lt;/p&gt;

&lt;p&gt;The third quartile can be calculated using:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;=QUARTILE.INC(tblProducts[Review],3)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Thresholds:&lt;/p&gt;

&lt;p&gt;Blank - Missing&lt;br&gt;
Review count below Review_Q3 - Normal Engagement&lt;br&gt;
Review count greater than or equal to Review_Q3 - Strong Engagement&lt;/p&gt;

&lt;p&gt;These thresholds provide a consistent way of grouping products and make it easier to compare them during the analysis stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis and Pivot Tables
&lt;/h2&gt;

&lt;p&gt;I used calculations, charts and Pivot Tables to summarize the cleaned data.&lt;br&gt;
Below were my findings for descriptive analysis:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgbsfl24sqvz4vqqyaejs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgbsfl24sqvz4vqqyaejs.png" alt=" " width="800" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here were my relationship analysis findings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr62srmn3xh0kye0tlau9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr62srmn3xh0kye0tlau9.png" alt=" " width="628" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Pivot Tables below represent product ranking analysis:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Top  5 Products by Rating &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpxdk1ejc8swl8crspqnr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpxdk1ejc8swl8crspqnr.png" alt=" " width="628" height="183"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bottom 5 Products by Rating&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkdksjlebcz7ur2kn81qj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkdksjlebcz7ur2kn81qj.png" alt=" " width="623" height="159"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Top 10 Products by Discount&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F71d5tjukejwqti4bjfx3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F71d5tjukejwqti4bjfx3.png" alt=" " width="636" height="258"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Top 10 Products by Reviews&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F09oocp3k217j16bxendo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F09oocp3k217j16bxendo.png" alt=" " width="625" height="252"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Products with High Discounts but Low Rating&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F59mb6lpukwgg6oez45ve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F59mb6lpukwgg6oez45ve.png" alt=" " width="634" height="317"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Products with many reviews but average ratings.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdoa6kcbjibhisa9dc4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdoa6kcbjibhisa9dc4g.png" alt=" " width="629" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dashboard Design and Slicer Connections
&lt;/h2&gt;

&lt;p&gt;The final dashboard is a combination of key Pivot Tables and charts in a single view, with slicers connected to allow filtering by rating category, discount category, price category and engagement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpfp98szfwrxb9kc1kfdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpfp98szfwrxb9kc1kfdc.png" alt=" " width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Finding
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Higher discounts do not mean more customer engagement.
Discount percentage and review count show essentially no correlation (r = -0.11).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Business Recommendations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sellers should not rely on discount depth alone to drive customer interest — engagement appears to depend on other factors not captured by discount size.&lt;/li&gt;
&lt;li&gt;High-review, low-rating products (e.g., the cordless vacuum cleaner) should be flagged for quality review, since visibility without satisfaction is a churn risk.&lt;/li&gt;
&lt;li&gt;Encourage more customers to leave ratings and reviews since half of the products lacks both rating and reviews.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Dataset size (116 rows) limits how confidently patterns can be generalized.&lt;/li&gt;
&lt;li&gt;No units-sold or revenue field thus analysis lacks a sales indicator.&lt;/li&gt;
&lt;li&gt;Nearly half of products are unrated, limiting the reliability of rating-based conclusions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This project demonstrates how Excel can transform a messy real-world dataset through data cleaning, formulas, PivotTables, and an interactive dashboard. The analysis revealed that discounts, reviews, and ratings do not necessarily correlate, highlighting the importance of testing assumptions with data.&lt;/p&gt;

&lt;p&gt;The full workbook, formulas, and dashboard are available in this &lt;a href="https://github.com/ClarenceG01/Jumia-Product-Performance" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; &lt;/p&gt;

</description>
      <category>analysis</category>
      <category>data</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Excel for Data Analytics: Fundamentals &amp; Data Cleaning</title>
      <dc:creator>Clarence Gatama Chege</dc:creator>
      <pubDate>Sat, 29 Aug 2026 20:11:16 +0000</pubDate>
      <link>https://dev.to/clarenceg01/excel-for-data-analytics-fundamentals-data-cleaning-31m9</link>
      <guid>https://dev.to/clarenceg01/excel-for-data-analytics-fundamentals-data-cleaning-31m9</guid>
      <description>&lt;p&gt;Microsoft Excel is one of the most important computer applications because of the key role it plays in many sectors. It is used in data entry, record keeping, project management,  business intelligence and data analysis.&lt;/p&gt;

&lt;p&gt;The data stored in Excel can sometimes be messy due to duplicate values, missing values, inconsistent text, incorrect data types for example having a text value in a column with numeric values etc, which can affect the accuracy of analysis. In data analytics, before the data can be analyzed, it needs to be cleaned and well structured.&lt;/p&gt;

&lt;p&gt;In this article we will explore the basics of Excel and also practical data-cleaning techniques. We will use the &lt;a href="https://www.kaggle.com/datasets/ahmedmohamed2003/cafe-sales-dirty-data-for-cleaning-training" rel="noopener noreferrer"&gt;Cafe Sales Dataset&lt;/a&gt; to demonstrate how to work with data in Excel and cleaning the data to prepare it for analysis.&lt;br&gt;
Download the dataset from the link above.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding The Dataset
&lt;/h2&gt;

&lt;p&gt;Let us explore the dataset first before diving into cleaning. Exploring helps us understand what exactly we are working with.&lt;br&gt;
The dataset has 8 columns in total and 10,000 rows of synthetic data representing sales transactions in a cafe. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column Name&lt;/th&gt;
&lt;th&gt;Data Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transaction ID&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Item&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quantity&lt;/td&gt;
&lt;td&gt;Numeric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price Per Unit&lt;/td&gt;
&lt;td&gt;Numeric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Spent&lt;/td&gt;
&lt;td&gt;Numeric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment Method&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction Date&lt;/td&gt;
&lt;td&gt;Date&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Below is a screenshot of the dataset when opened in Excel:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcrei3xxolsswn0vaw109.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcrei3xxolsswn0vaw109.png" alt="dataset opened on excel" width="799" height="419"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Working With Excel
&lt;/h2&gt;

&lt;p&gt;Before we dive into cleaning let's look into some Excel fundamentals that will help us work with this dataset.&lt;/p&gt;
&lt;h4&gt;
  
  
  Adjusting the column width
&lt;/h4&gt;

&lt;p&gt;You will note that when you open the dataset not all cell values are fully visible. Some values, for example values in column Transaction Date, are represented by # because the values are longer than the available column width. Adjusting the column width makes all values visible. Start by selecting all values in our dataset with &lt;code&gt;CTRL + A&lt;/code&gt;. Then in the &lt;strong&gt;Home Tab&lt;/strong&gt;, &lt;strong&gt;cell group&lt;/strong&gt;, select Format and choose &lt;code&gt;AutoFit Column Width&lt;/code&gt;.Excel automatically adjusts the width of each column based on the contents of the cells, making the values fully visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6nl6ld0rtsdbqj7ps99s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6nl6ld0rtsdbqj7ps99s.png" alt="dataset after adjusting column width" width="800" height="422"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Formatting the Header
&lt;/h4&gt;

&lt;p&gt;We can make our header row different from the other rows by making it bold, centering the text, and adding a background color. The header row is the row containing the column names. Select the first row with column names, in the &lt;strong&gt;Home tab&lt;/strong&gt; under &lt;strong&gt;Font group&lt;/strong&gt;  click &lt;strong&gt;B&lt;/strong&gt;. Alternatively you can use &lt;code&gt;CTRL + B&lt;/code&gt; to apply the bold.&lt;br&gt;
While still selecting the header row, in the &lt;strong&gt;Home tab&lt;/strong&gt; under &lt;strong&gt;Alignment&lt;/strong&gt;  select &lt;strong&gt;Center&lt;/strong&gt;.&lt;br&gt;
We can also add a background color using the Fill Color option in the &lt;strong&gt;Font group&lt;/strong&gt;. This is how the header row looks after formatting:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fappnwlbg20riad6h2dyx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fappnwlbg20riad6h2dyx.png" alt="dataset with formatted header" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Adding Freeze Panes
&lt;/h4&gt;

&lt;p&gt;As you scroll down the dataset you will note that you lose sight of the header row. This makes it hard to remember which column is which. That is where freeze pane comes in. What Freeze Panes does is that it ensures the header row is always at the top of the dataset. &lt;br&gt;
To freeze our header row first select the whole dataset, then under &lt;strong&gt;Window group&lt;/strong&gt; in &lt;strong&gt;View tab&lt;/strong&gt; select &lt;strong&gt;Freeze Panes&lt;/strong&gt; and choose &lt;strong&gt;Freeze Top Row&lt;/strong&gt;. Now as you scroll down the dataset, the header row always remains at the top. &lt;/p&gt;

&lt;p&gt;Also note that Freeze Panes can also be applied on the first column.&lt;/p&gt;
&lt;h4&gt;
  
  
  Sorting Data
&lt;/h4&gt;

&lt;p&gt;Sorting means arranging data in a specific order. In Excel, text data can be sorted alphabetically, from A to Z or from Z to A and numeric data can be sorted from largest to smallest or from smallest to largest. Date data can also be sorted chronologically, from the earliest date to the latest date or vice versa.&lt;br&gt;
For example in our dataset we can sort &lt;code&gt;Price Per Unit&lt;/code&gt; column. To do this, select any cell in the &lt;code&gt;Price Per Unit&lt;/code&gt; column, go to the &lt;strong&gt;Data&lt;/strong&gt; tab, and select Sort Smallest to Largest icon in the &lt;strong&gt;Sort &amp;amp; Filter&lt;/strong&gt; group. This will arrange the values from the lowest price to the highest price while keeping the other values in each row together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftkimje1p5aiu6ckcmlsu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftkimje1p5aiu6ckcmlsu.png" alt="Dataset with sorted column" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sorting makes it easier to examine the data and identify values that may require further investigation during the cleaning process.&lt;/p&gt;
&lt;h4&gt;
  
  
  Filtering
&lt;/h4&gt;

&lt;p&gt;Filtering allows us to display only the records that meet specific conditions while temporarily hiding the other records.&lt;/p&gt;

&lt;p&gt;For example, in our dataset, we can filter the Payment Method column to display only transactions where customers paid using Cash. To do this, select any cell in the dataset, go to the &lt;strong&gt;Data&lt;/strong&gt; tab, and select Filter from the &lt;strong&gt;Sort &amp;amp; Filter&lt;/strong&gt; group. A dropdown arrow will appear in each column header. Click the dropdown arrow in the Payment Method column, deselect Select All, select Cash, and click OK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzihbdodsiqxz6yxd1zwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzihbdodsiqxz6yxd1zwo.png" alt="Filtering dropdown" width="458" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Excel will now display only the transactions where the payment method is Cash and the other records will be hidden.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpisn4dpwqgxsl5ls87yy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpisn4dpwqgxsl5ls87yy.png" alt="dataset with column payment method filtered to cash" width="799" height="420"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Basic Formulas and Functions
&lt;/h4&gt;

&lt;p&gt;Excel formulas and functions allow us to perform calculations and extract useful information from a dataset.&lt;/p&gt;

&lt;p&gt;For example, we can calculate the total quantity of items sold in our dataset using the &lt;code&gt;SUM&lt;/code&gt; function. Since the &lt;code&gt;Quantity&lt;/code&gt; column contains the number of items purchased in each transaction, we can use the following formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=SUM(C2:C10001)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AVERAGE&lt;/code&gt; function can be used to calculate the average quantity of items purchased per transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=AVERAGE(C2:C10001)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other useful functions when exploring a dataset include &lt;code&gt;COUNT&lt;/code&gt;, &lt;code&gt;MIN&lt;/code&gt;, and &lt;code&gt;MAX&lt;/code&gt;. For example, &lt;code&gt;COUNT&lt;/code&gt; can be used to determine the number of numeric values in a column, while &lt;code&gt;MIN&lt;/code&gt; and &lt;code&gt;MAX&lt;/code&gt; can help identify the smallest and largest values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Cleaning
&lt;/h2&gt;

&lt;p&gt;Having gone through some of Excel fundamentals, we can now move on to cleaning the dataset. Data cleaning is the process of identifying and correcting inaccurate, incomplete, inconsistent, or irrelevant data to improve the quality of a dataset.&lt;/p&gt;

&lt;h4&gt;
  
  
  Removing Duplicate Records
&lt;/h4&gt;

&lt;p&gt;The first step in cleaning our dataset is to remove any duplicate records. In our dataset, a duplicate record would be a cafe sale that has the same value in all fields.&lt;/p&gt;

&lt;p&gt;We can do this by selecting a cell in the dataset, then in the &lt;strong&gt;Data&lt;/strong&gt; tab under the &lt;strong&gt;Data Tools&lt;/strong&gt; group click &lt;code&gt;Remove Duplicates&lt;/code&gt;. A dialog box appears showing all the columns in the dataset. Since we are looking for records that are identical across all fields, ensure all the columns are selected and click OK.&lt;/p&gt;

&lt;p&gt;Our dataset does not have any duplicate rows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftmlk648af3a6je059pi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftmlk648af3a6je059pi2.png" alt="dataset no duplicate values" width="800" height="422"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Missing Values
&lt;/h4&gt;

&lt;p&gt;To identify missing values, we will make use of Filter. Select any cell in the dataset, in the &lt;strong&gt;Home&lt;/strong&gt; tab, under the &lt;strong&gt;Editing&lt;/strong&gt; group, select the &lt;strong&gt;Sort&amp;amp;Filter&lt;/strong&gt; dropdown and click &lt;strong&gt;Filter&lt;/strong&gt;. You will see a filter dropdown appear in each column header. When clicked, this filter dropdown shows the unique values contained in that column. To identify missing values, we can look for the (Blanks) option in the list.&lt;/p&gt;

&lt;p&gt;For example, we can check the &lt;code&gt;Item&lt;/code&gt; column for missing values. Click the filter dropdown in the &lt;code&gt;Item&lt;/code&gt; column. We can now see all the unique values in the column, &lt;strong&gt;(Blanks)&lt;/strong&gt; included. This shows that there are missing values in the &lt;code&gt;Item&lt;/code&gt; column.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7elhzpk5nlpythp6ixlc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7elhzpk5nlpythp6ixlc.png" alt="Item column filter dropdown" width="283" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To handle such missing values, we first check if we can determine the missing value from the other values in the row. Since we cannot do that, we will use &lt;strong&gt;Find &amp;amp; Replace&lt;/strong&gt; to change them to &lt;code&gt;Unknown&lt;/code&gt;. Select the &lt;code&gt;Item&lt;/code&gt; column then press &lt;strong&gt;CTRL + H&lt;/strong&gt;. A dialog appears. Leave the &lt;code&gt;Find&lt;/code&gt; field empty and write &lt;code&gt;Unknown&lt;/code&gt; in the &lt;code&gt;Replace&lt;/code&gt; field then click &lt;code&gt;Replace All&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3io8p0y1y7e775jybqz1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3io8p0y1y7e775jybqz1.png" alt=" " width="516" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A total of 333 missing values in the &lt;strong&gt;Item&lt;/strong&gt; column were changed to &lt;code&gt;Unknown&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbvt62uq6ezd1pqgrfv17.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbvt62uq6ezd1pqgrfv17.png" alt=" " width="799" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Correcting Data Types
&lt;/h4&gt;

&lt;p&gt;Another important part of data cleaning is ensuring that each column contains the appropriate type of data. The data type determines how Excel interprets and works with the values.&lt;/p&gt;

&lt;p&gt;For example in our dataset, Quantity, Price Per Unit, and Total Spent should contain numeric values because they represent quantities and monetary amounts. Transaction Date should contain date values, while columns such as Item, Payment Method, and Location contain text.&lt;/p&gt;

&lt;p&gt;A good example of a column with data-type problems is in the &lt;code&gt;Price Per Unit&lt;/code&gt; column. It should contain numeric values but we have identified it has values &lt;code&gt;UNKNOWN&lt;/code&gt; and &lt;code&gt;ERROR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7h2ls46oicx2u0uyosgh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7h2ls46oicx2u0uyosgh.png" alt=" " width="393" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect way of cleaning this is by using the &lt;strong&gt;Find &amp;amp; Replace&lt;/strong&gt; we used earlier on. Find cells with &lt;code&gt;UNKNOWN&lt;/code&gt; then replace with blanks. Do the same for &lt;code&gt;ERROR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Quantity&lt;/code&gt; column also contains the text values &lt;code&gt;UNKNOWN&lt;/code&gt; and &lt;code&gt;ERROR&lt;/code&gt;, which can be replaced with blanks using the same approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we explored some of the Excel fundamentals that are useful when working with datasets, including adjusting column widths, formatting headers, freezing panes, sorting, filtering, and using basic formulas and functions.&lt;/p&gt;

&lt;p&gt;We then applied these concepts to clean a cafe sales dataset containing 10,000 records. We removed duplicate records, identified missing values using filters, and handled missing and inconsistent values such as &lt;code&gt;UNKNOWN&lt;/code&gt; and &lt;code&gt;ERROR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Data cleaning is an important step before performing analysis because the quality of the data directly affects the quality of the insights gotten.&lt;/p&gt;

</description>
      <category>datacleaning</category>
      <category>excel</category>
    </item>
    <item>
      <title>Understanding the Git Workflow: From Local Folder to Remote Repository</title>
      <dc:creator>Clarence Gatama Chege</dc:creator>
      <pubDate>Sat, 22 Aug 2026 19:50:08 +0000</pubDate>
      <link>https://dev.to/clarenceg01/understanding-the-git-workflow-from-local-folder-to-remote-repository-4b54</link>
      <guid>https://dev.to/clarenceg01/understanding-the-git-workflow-from-local-folder-to-remote-repository-4b54</guid>
      <description>&lt;p&gt;For quite a while I have used Git without knowing what happens behind the scenes. I mastered using all commands from initializing, adding, committing to  pushing code to Github but I couldn't explain what was happening to the files after each command. In this article we are going to cover what Git is, the git commands used when working in a Git repository and the four areas Git uses to track and manage your files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git and Github
&lt;/h2&gt;

&lt;p&gt;Git and Github are not the same thing.&lt;br&gt;
Git is a free, open source tool that helps you save and manage different versions of your files and code. Git sits silently in your project folder, keeping track of every change that happens.&lt;br&gt;
Github on the other hand is a platform that hosts your project folders and repositories online. This way other people can access your code, collaborate with you or review your work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git Repository
&lt;/h2&gt;

&lt;p&gt;A Git repository is a folder that Git lives in and tracks for changes, this includes, every addition, deletion, and edit made in that folder. To initialize a repository (to make Git track your folder), you use the command git init.&lt;/p&gt;

&lt;p&gt;This is how it works. You have a folder &lt;code&gt;article&lt;/code&gt; that has a file &lt;code&gt;README.md&lt;/code&gt; and a folder &lt;code&gt;first-article&lt;/code&gt; that you will be working on.Right now, this is just a standard folder on your computer. Git has no idea it exists.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdlsivcqlef82pp48v7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdlsivcqlef82pp48v7i.png" alt="Folder article" width="795" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you run git init, Git creates a hidden .git folder inside it. From this point on, your article folder serves as your &lt;strong&gt;Working Directory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq76wswo3yzcye5a9v7j1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq76wswo3yzcye5a9v7j1.png" alt="Initializing git" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffn7d2upkxnugkrn4uk8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffn7d2upkxnugkrn4uk8z.png" alt="Working directory" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Git Workflow
&lt;/h2&gt;

&lt;p&gt;In Git, files move through four distinct workflow areas or stages. Lets look into each stage:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Working Directory&lt;/strong&gt;&lt;br&gt;
This is the folder where Git has been initialized and where you make changes to your files, in our case the &lt;code&gt;article&lt;/code&gt; folder. New or updated files cannot be tracked by Git until you stage and commit them. Using git status we can check the current state of your working directory. For example in our current working directory, git status shows we have a file &lt;code&gt;README.md&lt;/code&gt; that is untracked meaning Git is aware of the file but it is not currently keeping a record of its changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fteuszkslb599csytwi9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fteuszkslb599csytwi9u.png" alt="git-status" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Staging Area&lt;/strong&gt;&lt;br&gt;
To make Git track the different versions of our files we use the command git add. Git add takes a copy of your current files from your working directory to the staging area.&lt;br&gt;
In our example, lets make changes to &lt;code&gt;README.md&lt;/code&gt; file and also add a new text file in folder &lt;code&gt;first-article&lt;/code&gt;. These changes will still be untracked until we use the git add command as in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3zfkvm6v9ms6tvv2sv2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3zfkvm6v9ms6tvv2sv2v.png" alt="git-article-5" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make git track this changes we use the git add command. In this case we can run either:&lt;br&gt;
&lt;code&gt;git add README.md first-article/&lt;/code&gt; to add the two or we run &lt;code&gt;git add .&lt;/code&gt; to add all the untracked files.&lt;/p&gt;

&lt;p&gt;Now when we run git status we'll see that git can now track our files and can confirm that &lt;code&gt;README.md&lt;/code&gt; &amp;amp; &lt;code&gt;first-article/&lt;/code&gt; have changes to be committed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feq3jr4rh9xsr8dkadquf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feq3jr4rh9xsr8dkadquf.png" alt="git add . image" width="799" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Local Repository&lt;/strong&gt;&lt;br&gt;
The git commit command takes the snapshot from your Staging Area and moves it into the hidden .git folder. The git commit command includes a message that describes what the commit is about&lt;br&gt;
&lt;code&gt;git commit -m "Commit message"&lt;/code&gt;&lt;br&gt;
Running git commit packages your changes and outputs exactly what was saved. Running git status and seeing “nothing to commit, working tree clean” means the staging area is empty, and all your current changes have been committed and safely backed up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa9f13poi287eemktcciz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa9f13poi287eemktcciz.png" alt="git commit" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Remote Repository&lt;/strong&gt;&lt;br&gt;
After committing, your changes are saved only in your local Git repository. Use the &lt;code&gt;git push&lt;/code&gt; command to upload those commits to a remote repository, such as GitHub or Bitbucket, making them accessible to others.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faqdg11uqeask1a1uct2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faqdg11uqeask1a1uct2u.png" alt="git push" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
In conclusion, understanding Git’s four tracking areas gives you a clearer picture of how your project files are managed. Instead of running commands without knowing what happens behind the scenes, you can now understand how your work moves from the Working Directory to the Staging Area, then to the Local Repository, and finally to the Remote Repository, giving you control over your project history and ensuring your work is safely backed up. One important advantage of Git is that you can access previous versions of your work. Overall, Git provides a reliable and organized way to manage, track, and protect your projects.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
    </item>
    <item>
      <title>Building a shopping cart using React, Redux toolkit</title>
      <dc:creator>Clarence Gatama Chege</dc:creator>
      <pubDate>Sun, 26 May 2024 12:59:55 +0000</pubDate>
      <link>https://dev.to/clarenceg01/building-a-shopping-cart-using-react-redux-toolkit-1fd5</link>
      <guid>https://dev.to/clarenceg01/building-a-shopping-cart-using-react-redux-toolkit-1fd5</guid>
      <description>&lt;p&gt;In this article, we'll walk through the process of creating an simple e-commerce application with the Fake store API using React redux and react toolkit, with a focus on implementing a shopping cart. By the end of this tutorial, you will have a functional application with the following features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A product listing page displaying products available for purchase.&lt;/li&gt;
&lt;li&gt;The ability to add items to the shopping cart.&lt;/li&gt;
&lt;li&gt;A shopping cart page where users can view, update quantity, and remove items from their cart.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's get started !!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create React project using vite:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest shopping-cart -- --template react
cd shopping-cart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;We will create three components: Cart, Navbar, and Shop components.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixx1vnwpyki4k11sh108.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixx1vnwpyki4k11sh108.png" alt="Project folder structure" width="204" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3.We will use fakestore api to get products for our project. Below is our initial Shop.jsx:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Shop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProducts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://fakestoreapi.com/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"shop"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"details-div"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"title-price"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Add to cart&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Shop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;We now have the products set up so we'll dive into redux toolkit.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redux Toolkit&lt;/strong&gt;&lt;br&gt;
Redux toolkit is a library that helps us write redux logic. It offers tools that simplify Redux setup and use, reducing boilerplate and enhancing code maintainability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redux Store&lt;/strong&gt;&lt;br&gt;
A redux store is a central repository that stores all the states of an application. Store is made up of slices. &lt;br&gt;
To create our own store we need to install redux toolkit and react redux:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @reduxjs/toolkit react-redux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create a store.js file inside redux folder inside src folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrkb69pxb0odj8yn6xvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrkb69pxb0odj8yn6xvm.png" alt="Project folder structure" width="205" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In store.js we will create a redux store (store) using &lt;em&gt;&lt;strong&gt;configureStore&lt;/strong&gt;&lt;/em&gt; provided by reduxjs and export it.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;configureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To make the store accessible to our application, we need to wrap our App component inside &lt;strong&gt;&lt;em&gt;Provider&lt;/em&gt;&lt;/strong&gt; that allows a prop called store which we will set to our store. This is done inside our main.jsx (could be index.js if you created app using CRA)&lt;br&gt;
Provider is a component that allows Redux store to be made available to all components.&lt;/p&gt;

&lt;p&gt;Here is how our main.jsx looks like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App.jsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./redux/store.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Redux Slice&lt;/strong&gt;&lt;br&gt;
A slice is a part of the app's state that is managed by a specific reducer and contains actions related to the state. &lt;br&gt;
For our project we'll create productsSlice.js file where we'll use createSlice function to create productsSlice.&lt;br&gt;
createSlice accepts an object with the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name -defines the slice's name.&lt;/li&gt;
&lt;li&gt;initialState -sets up the starting state for the slice when the Redux store is first created.&lt;/li&gt;
&lt;li&gt;reducers - these are functions that handle the logic for updating the state. We will create our first reducer &lt;em&gt;&lt;strong&gt;addProductToCart&lt;/strong&gt;&lt;/em&gt;.
We will also export actions and reducer created by our createSlice.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;addProductToCart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addProductToCart&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;productsSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;productsSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For our productsSlice to be available to the app, we will import reducer created by createSlice inside Store.js and add it to the reducer object of our store. &lt;/p&gt;

&lt;p&gt;Here's how our store.js will be:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;productReducer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./productsSlice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;configureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To be able to add a product to cart we will use two react-redux hooks:&lt;strong&gt;&lt;em&gt;useSelector&lt;/em&gt;&lt;/strong&gt; and*&lt;em&gt;&lt;em&gt;useDispatch&lt;/em&gt;&lt;/em&gt;*&lt;br&gt;
useSelector allows components to get(select) part of the state that is required. useDispatch allows components to dispatch actions to the redux store. &lt;br&gt;
Inside Shop.jsx we will import useDispatch from react-redux, invoke it and store in a variable dispatch. Variable dispatch will then be used to dispatch action &lt;em&gt;addProductToCart&lt;/em&gt; when button 'Add to cart' is clicked. &lt;/p&gt;

&lt;p&gt;Here's our Shop.jsx:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addProductToCart&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../redux/productsSlice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Shop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProducts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://fakestoreapi.com/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"shop"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"details-div"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"title-price"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
              &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nf"&gt;addProductToCart&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              Add to cart
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Shop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now that the action is dispatched, the products are successfully added to cart which is part of the state. To display the added products, we will use &lt;em&gt;useSelector&lt;/em&gt; inside Cart.jsx to get/extract/select cart which is part of our productsSlice.&lt;/p&gt;

&lt;p&gt;Here's our Cart.jsx:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSelector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"cart-component"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"cart-card"&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;REMOVE&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button-in-cart"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;-&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;$:&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      ;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To display the count of products in our navbar, we will also get cart using useSelector inside Navbar.jsx. Cart being an array, we will use its length to get the count of products added to cart.&lt;/p&gt;

&lt;p&gt;Here's our Navbar.jsx:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSelector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"cart-component"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"cart-card"&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;REMOVE&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button-in-cart"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;-&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;$:&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      ;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Using redux we have been able to create a store (source of truth 😅) allowing different components to access the state. In the next part we will ensure that when a similar product is clicked a number of times , it's not added to cart as multiple cart products by adding a quantity property. We will also add other actions such as increase quantity of product, decrease quantity of product, remove product and clear cart.&lt;/p&gt;

&lt;p&gt;Source code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ClarenceG01" rel="noopener noreferrer"&gt;
        ClarenceG01
      &lt;/a&gt; / &lt;a href="https://github.com/ClarenceG01/shopping-cart-redux" rel="noopener noreferrer"&gt;
        shopping-cart-redux
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;React + Vite&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.&lt;/p&gt;

&lt;p&gt;Currently, two official plugins are available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md" rel="noopener noreferrer"&gt;@vitejs/plugin-react&lt;/a&gt; uses &lt;a href="https://babeljs.io/" rel="nofollow noopener noreferrer"&gt;Babel&lt;/a&gt; for Fast Refresh&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vitejs/vite-plugin-react-swc" rel="noopener noreferrer"&gt;@vitejs/plugin-react-swc&lt;/a&gt; uses &lt;a href="https://swc.rs/" rel="nofollow noopener noreferrer"&gt;SWC&lt;/a&gt; for Fast Refresh&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ClarenceG01/shopping-cart-redux" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>react</category>
      <category>api</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
