Breaking up methods VS time complexity

Breaking up methods/functions to do one thing only is considered a good practice as it leads to more maintainable and readable code but it increase time complexity in many cases . How to achieve both ie. optimum time complexity while still keeping readability/maintainability ?

As illustrated in this dummy example . I want to find count of Js in a string but also want to check if addition of all positive integers is even or odd . Two separate methods for these 2 different tasks leads to excellent readability but increases time complexity . Combining them in a method leads to very low readability but decreases time complexity.

class UserText{ 
    constructor(userProvidedText){
     this.content = userProvidedText;
    }
    _findCharCount(char){
      let count = 0;
      for (let i = 0; i < this.content.length; i++) {
        if (this.content[i] === char) {
          count++;
        }
    }   
    return count
    }

    _isDigit(char) {
        return !isNaN(parseInt(char));
      }

    // Excellent Readability and maintainability 
    findJs(){
        return this._findCharCount("J")
        }
    
    // Excellent Readability and maintainability 
    CheckIfDigitsAdditionEven(){ 
        let count = 0;
        for (let i = 0; i < this.content.length; i++) {
          console.log(this.content[i])
          if (!this._isDigit(this.content[i])){ 
            continue
          }
          count+=parseInt(this.content[i]); 
        }   
        if (count % 2 === 0){ 
            return true
        } 
        return false
    }

    // Combining functionalities in 1 function . Not readable. Not maintainable But time complexity is decreased. 
    CheckIfDigitsAdditionEvenAndCountJs(char){ 
        let count1 = 0;
        let count2 = 0;

        let res1 = false;

        for (let i = 0; i < this.content.length; i++) {
          if (this.content[i] === char) {
            count2++;
          }
          if (this._isDigit(this.content[i])){ 
            count1+=parseInt(this.content[i]); 
          }
        }   
        if (count1 % 2 === 0){ 
            res1 = true
        } 
        return [res1, count2]

    }
} 
ut = new UserText("SOME..STRING..12311.HHIIJJJJKKKLL ")

/*
Breaking up functions and using them one at a time . One method , one purpose.. 
Maintainable and readable but time complexity is  O(2n) .  
*/

JCount=ut.findJs() // Time complexity O(n)
evenCheck=ut.CheckIfDigitsAdditionEven() //Time complexity O(n)


/*
Using 1 function for the task  . One method , multi-purposed.. 
Maintainable and readable is very low but  time complexity is  O(n) .  
*/

cr=ut.CheckIfDigitsAdditionEvenAndCountJs("J") //Time complexity O(n)

// Some custom logic here . 
console.log(JCount, evenCheck)
console.log(cr)

Time series representation of Map Chart using amCharts

I am creating a Map chart using amCharts5.

The data to be drawn includes latitude and longitude, title, and date as shown below.

var spots = [
{"date":1,"title":"point1","latitude":42.1613,"longitude":139.452},{"date":1,"title":"point2","latitude":41.3597,"longitude":139.806},
{"date":1,"title":"point3","latitude":40.3597,"longitude":138.605},
{"date":2,"title":"point1","latitude":42.1613,"longitude":139.452},
{"date":2,"title":"point2","latitude":41.3597,"longitude":139.806},
];

Drawing all the data was successful with the code below.

<script>
    var spots = [{
            "date": 1,
            "title": "point1",
            "latitude": 42.1613,
            "longitude": 139.452
        },
        {
            "date": 1,
            "title": "point2",
            "latitude": 41.3597,
            "longitude": 139.806
        },
        {
            "date": 1,
            "title": "point3",
            "latitude": 40.3597,
            "longitude": 138.605
        },
        {
            "date": 2,
            "title": "point1",
            "latitude": 42.1613,
            "longitude": 139.452
        },
        {
            "date": 2,
            "title": "point2",
            "latitude": 41.3597,
            "longitude": 139.806
        },
    ];
    am5.ready(function() {
        var root = am5.Root.new("spcsmap");
        root.setThemes([
            am5themes_Animated.new(root)
        ]);

        var chart = root.container.children.push(am5map.MapChart.new(root, {
            panX: "rotateX",
            panY: "translateY",
            projection: am5map.geoMercator(),
            homeZoomLevel: 1.8,
            homeGeoPoint: {
                longitude: 135,
                latitude: 35
            }
        }));

        chart.set("zoomControl", am5map.ZoomControl.new(root, {}));

        loadGeodata('JP');

        var polygonSeries = chart.series.push(am5map.MapPolygonSeries.new(root, {}));

        var pointSeries = chart.series.push(am5map.ClusteredPointSeries.new(root, {}));

        pointSeries.set("clusteredBullet", function(root) {
            var container = am5.Container.new(root, {
                cursorOverStyle: "pointer"
            });

            var circle1 = container.children.push(am5.Circle.new(root, {
                radius: 8,
                tooltipY: 0,
                fill: am5.color(0xff8c00)
            }));

            var circle2 = container.children.push(am5.Circle.new(root, {
                radius: 12,
                fillOpacity: 0.3,
                tooltipY: 0,
                fill: am5.color(0xff8c00)
            }));

            var circle3 = container.children.push(am5.Circle.new(root, {
                radius: 16,
                fillOpacity: 0.3,
                tooltipY: 0,
                fill: am5.color(0xff8c00)
            }));

            var label = container.children.push(am5.Label.new(root, {
                centerX: am5.p50,
                centerY: am5.p50,
                fill: am5.color(0xffffff),
                populateText: true,
                fontSize: "8",
                text: "{value}"
            }));
            container.events.on("click", function(e) {
                pointSeries.zoomToCluster(e.target.dataItem);
            });

            return am5.Bullet.new(root, {
                sprite: container
            });
        });

        pointSeries.bullets.push(function() {
            var circle = am5.Circle.new(root, {
                radius: 6,
                tooltipY: 0,
                fill: am5.color(0xff8c00),
                tooltipText: "{title}"
            });

            return am5.Bullet.new(root, {
                sprite: circle
            });
        });

        var container = chart.children.push(am5.Container.new(root, {
            y: am5.p100,
            centerX: am5.p50,
            centerY: am5.p100,
            x: am5.p50,
            width: am5.percent(90),
            layout: root.horizontalLayout,
            paddingBottom: 10
        }));

        var first = 1;
        var last = 7;
        var slider = container.children.push(am5.Slider.new(root, {
            orientation: "horizontal",
            start: 0,
            centerY: am5.p50
        }));

        slider.startGrip.get("icon").set("forceHidden", true);
        slider.startGrip.set("label", am5.Label.new(root, {
            text: first + "",
            paddingTop: 0,
            paddingRight: 0,
            paddingBottom: 0,
            paddingLeft: 0
        }));

        slider.events.on("rangechanged", function() {
            var date = first + Math.round(slider.get("start", 0) * (last - first));
            slider.startGrip.get("label").set("text", date + "");
        });

        chart.appear(1000, 100);

        polygonSeries.events.on("datavalidated", function() {
            chart.goHome();
        });

        const spotDatas = [];
        am5.object.each(spots, function(key, datas) {
            spotDatas.push({
                geometry: {
                    type: "Point",
                    coordinates: [datas.longitude, datas.latitude]
                },
                title: datas.title
            });
        });
        pointSeries.data.setAll(spotDatas);

        function loadGeodata(country) {
            chart.set("projection", am5map.geoMercator());

            currentMap = am5geodata_data_countries2[country]["maps"][0];

            am5.net.load("https://cdn.amcharts.com/lib/5/geodata/json/" + currentMap + ".json", chart).then(function(result) {
                var geodata = am5.JSONParser.parse(result.response);
                var data = [];
                for (var i = 0; i < geodata.features.length; i++) {
                    data.push({
                        id: geodata.features[i].id,
                    });
                }
                polygonSeries.set("geoJSON", geodata);
                polygonSeries.data.setAll(data);
            });
        }
    });
</script>

What I would like to achieve from now on is to set up a slider that corresponds to the date and switch the displayed data depending on the slider value.

I thought it could be achieved with the code below, but an error occurred.

<script>
var spots = [
    {"date":1,"title":"point1","latitude":42.1613,"longitude":139.452},
    {"date":1,"title":"point2","latitude":41.3597,"longitude":139.806},
    {"date":1,"title":"point3","latitude":40.3597,"longitude":138.605},
    {"date":2,"title":"point1","latitude":42.1613,"longitude":139.452},
    {"date":2,"title":"point2","latitude":41.3597,"longitude":139.806},
];
am5.ready(function() {
    var root = am5.Root.new("spcsmap");
    root.setThemes([
        am5themes_Animated.new(root)
    ]);

    var chart = root.container.children.push(am5map.MapChart.new(root, {
        panX: "rotateX",
        panY: "translateY",
        projection: am5map.geoMercator(),
        homeZoomLevel: 1.8,
        homeGeoPoint: {
            longitude: 135,
            latitude: 35
        }
    }));

    chart.set("zoomControl", am5map.ZoomControl.new(root, {}));

    loadGeodata('JP');

    var polygonSeries = chart.series.push(am5map.MapPolygonSeries.new(root, {}));

    var pointSeries = chart.series.push(am5map.ClusteredPointSeries.new(root, {}));

    pointSeries.set("clusteredBullet", function(root) {
        var container = am5.Container.new(root, {
            cursorOverStyle: "pointer"
        });

        var circle1 = container.children.push(am5.Circle.new(root, {
            radius: 8,
            tooltipY: 0,
            fill: am5.color(0xff8c00)
        }));

        var circle2 = container.children.push(am5.Circle.new(root, {
            radius: 12,
            fillOpacity: 0.3,
            tooltipY: 0,
            fill: am5.color(0xff8c00)
        }));

        var circle3 = container.children.push(am5.Circle.new(root, {
            radius: 16,
            fillOpacity: 0.3,
            tooltipY: 0,
            fill: am5.color(0xff8c00)
        }));

        var label = container.children.push(am5.Label.new(root, {
            centerX: am5.p50,
            centerY: am5.p50,
            fill: am5.color(0xffffff),
            populateText: true,
            fontSize: "8",
            text: "{value}"
        }));
        container.events.on("click", function(e) {
            pointSeries.zoomToCluster(e.target.dataItem);
        });

        return am5.Bullet.new(root, {
            sprite: container
        });
    });

    pointSeries.bullets.push(function() {
        var circle = am5.Circle.new(root, {
            radius: 6,
            tooltipY: 0,
            fill: am5.color(0xff8c00),
            tooltipText: "{title}"
        });

        return am5.Bullet.new(root, {
            sprite: circle
        });
    });

    var container = chart.children.push(am5.Container.new(root, {
        y: am5.p100,
        centerX: am5.p50,
        centerY: am5.p100,
        x: am5.p50,
        width: am5.percent(90),
        layout: root.horizontalLayout,
        paddingBottom: 10
    }));

    var first = 1;
    var last = 7;
    var slider = container.children.push(am5.Slider.new(root, {
        orientation: "horizontal",
        start: 0,
        centerY: am5.p50
    }));

    slider.startGrip.get("icon").set("forceHidden", true);
    slider.startGrip.set("label", am5.Label.new(root, {
        text: first + "",
        paddingTop: 0,
        paddingRight: 0,
        paddingBottom: 0,
        paddingLeft: 0
    }));

    slider.events.on("rangechanged", function() {
        var date = first + Math.round(slider.get("start", 0) * (last - first));
        slider.startGrip.get("label").set("text", date + "");
        updateSpcs(date);
    });

    chart.appear(1000, 100);

    polygonSeries.events.on("datavalidated", function() {
        chart.goHome();
    });

    function loadGeodata(country) {
        chart.set("projection", am5map.geoMercator());

        currentMap = am5geodata_data_countries2[country]["maps"][0];

        am5.net.load("https://cdn.amcharts.com/lib/5/geodata/json/" + currentMap + ".json", chart).then(function(result) {
            var geodata = am5.JSONParser.parse(result.response);
            var data = [];
            for (var i = 0; i < geodata.features.length; i++) {
                data.push({
                    id: geodata.features[i].id,
                });
            }
            polygonSeries.set("geoJSON", geodata);
            polygonSeries.data.setAll(data);
        });
    }

    function updateSpcs(date) {
        const spotDatas = [];
        am5.object.each(spots, function(key, datas) {
            var dataDate = datas.date;
            if (dataDate == date) {
                spotDatas.push({
                    geometry: {
                        type: "Point",
                        coordinates: [datas.longitude, datas.latitude]
                    },
                    title: datas.title
                });
            }
        });
        pointSeries.data.setAll(spotDatas);
    }
});
</script>

The contents of the error are as follows.

Uncaught Error: EventDispatcher is disposed

What should I modify to express what I want to achieve?

I tried various codes based on the amcharts documentation, but none of them worked, and the above is the closest to completion.

Is there any way to convert current date and time of Bangladesh into Bangla Language using JavaScript?

I have a time like “Sat Feb 10 2024 15:33:51 GMT+0600 (Bangladesh Standard Time)” . I want to convert that time into Bangla language. For example সময়: ০৯:৫৯, মঙ্গলবার, ২৬ ডিসেম্বর, ২০২৩ ১২ পৌষ ১৪৩০ বঙ্গাব্দ, ০৯:৫৯ পূর্বাহ্ন

how to do this using JavaScript?

Error fro react-router-dom when refreshing the web page

I have a page with job offers at http://localhost:3000/offers. When I go to any vacancy, eg. http://localhost:3000/offers/1 where 1 is a parameter, I am redirected to the page with the offer.

However if I refresh the page, I get an error:
GET http://localhost:3000/offers/main.53268a2871a415dae5af.js net::ERR_ABORTED 404 (Not Found)
1:11 Refused to execute script from ‘http://localhost:3000/offers/main.53268a2871a415dae5af.js
This is because its MIME type (‘text/html’) is not executable, and strict MIME type checking is enabled.

Perhaps I configured something wrong in webpack.config.ts.

I would be very grateful for any help!

Full source code: https://github.com/nikitazapekin/worklist-application

Perhaps I configured something wrong in webpack.config.ts.

How i can stop polling function for that particular API that runs background job

I have used This common polling function for running background jobs.


export const poll = ({
  fn = () => {},
  validate = (result) => !!result,
  interval = 1000,
  maxAttempts = 15,
}) => {
  let attempts = 1;
  // eslint-disable-next-line consistent-return
  const executePoll = async (resolve, reject) => {
    try {
      const result = await fn();
      attempts += 1;

      if (validate(result)) {
        return resolve(result);
      } else if (maxAttempts && attempts > maxAttempts) { // eslint-disable-line no-else-return
        return reject(new Error('Exceeded max attempts'));
      } else {
        setTimeout(executePoll, interval, resolve, reject);
      }
    } catch (error) {
      setTimeout(executePoll, interval, resolve, reject);
    }
  };

  return new Promise(executePoll);
};

I have called this fun on click of button for running polling

poll({
          fn: () =>  some API call ex. api.get(url),
          validate: (res) => !res?.data?.lesson_id,
          interval: 5000,
          maxAttempts: 30,
        })

The issue is that after running the above fun in UI I am showing a delete icon on clicking that delete icon I am removing the element for which the above background job is running.

So I want to stop the above polling function after successful deletion.

Note: I have the delete function on that same page so I have control over delete when it is successful.

Any suggestion on what can I change in my common polling function? Thanks.

Classes duplication with css modules and extractCSS in Nuxt 2

I use nuxt 2 with css modules and when i use one css module with multiple components it results in then multiple css files have same class

Hello.vue

<template>
  <div :class="$style.title">Hello, World</div>
  <div :class="$second.secondClass">Hello, World</div>
</template>
<script>
export default {}
</script>
<style module src="./hello.css"></style>
<style module="$second" src="./uncommon.css"></style>

Goodbye.vue

<template>
  <div :class="$style.title">Goodbye, World</div>
  <div :class="$second.secondClass">Goodbye, World</div>
</template>
<script>
export default {}
</script>
<style module src="./goodbye.css"></style>
<style module="$second" src="./uncommon.css"></style>

and as a result i have two files with content

enter image description here

enter image description here

where in both files i have same class .YT3xv

Can i do something about it? in perfect scenario i would like to extract classes like that in separate file

Custom timeline slider using jquery

I am currently developing a custom timeline slider design, and I have encountered a challenge. While I haven’t implemented any code yet, my plan is to create a timeline slider resembling the attached image. However, I’m facing difficulty figuring out how to draw a vertical dotted line, as illustrated in the attached image. I intend to achieve this using jQuery in combination with any slider library. I would appreciate any guidance or solutions offered for this specific issue.

Thank you.

enter image description here

I can’t use ES6 module with typescript in my Node.js project

I want to use both typescript and the es6 module in my Node.js project, and in addition, I want the files compiled to the javascript language to have the es6 module.

This is my package.json :

{
  "name": "arep",
  "version": "1.0.0",
  "description": "",
  "main": "app.ts",
  "type": "module",
  "scripts": {
    "start": "nodemon --exec ts-node app.ts",
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.4.1",
    "express": "^4.18.2",
    "mysql2": "^3.9.1"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "@types/node": "^20.11.17",
    "nodemon": "^3.0.3",
    "ts-node": "^10.9.2",
    "typescript": "^5.3.3"
  }
}

And this is my tscongif.json :

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  }
}

Here is the error I get :

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /home/rznkolds/Desktop/Standart/2- Development/2- Applications/1- NodeJS/arep/app.ts
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:160:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:203:36)
    at defaultLoad (node:internal/modules/esm/load:141:22)
    at async nextLoad (node:internal/modules/esm/hooks:865:22)
    at async nextLoad (node:internal/modules/esm/hooks:865:22)
    at async Hooks.load (node:internal/modules/esm/hooks:448:20)
    at async handleMessage (node:internal/modules/esm/worker:196:18) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

When I do this with the commonjs module, everything works fine, but when I do it with the es6 module, I get this error.
I couldn’t find much resources about this error. In many places, people solved the problem by turning it into the CommonJS module.

Only the last v-for element is updated when all of them should

3 page links in the nav should be displayed in 3 languages by user choice. In the initial page load it works fine. Then click one language change link then again it works fine. But when I click a language change link another time only the last link’s language changes when all of them should change language. What is causing this problem and how to fix it?

app.vue:

<template>
    <nav class="navbar">
        <NuxtLink class="pagelink" :key="page.slug" v-for="page in strings.pages" :href="'/' + page.slug">{{ page.name[lang] }}</NuxtLink>
        <Languages />
    </nav>
</template>
<script>
import Languages from "./components/languages.vue"
import languages from "../services/languages"
export default {
    name: "Navbar",
    data() {
        return {
            open: false,
            strings: {
                pages: [
                    {
                        slug: 'home',
                        name: { az: "Əsas", ru: "Главная", en: "Home" }
                    },
                    {
                        slug: 'about',
                        name: { az: "Haqqımızda", ru: "О нас", en: "About" }
                    },
                    {
                        slug: 'contact',
                        name: { az: "Əlaqə", ru: "Связаться", en: "Contact Us" }
                    }
                ]
            }
        }
    },
    computed: {
        lang() {
            return languages(this)
        }
    }
}
</script>

<style>
* {
    margin: 10px;
}
</style>

languages.vue:

<template>
    <div class="languages">
        <NuxtLink :to="route.path + '?hl=az'">AZ</NuxtLink>
        <NuxtLink :to="route.path + '?hl=ru'">RU</NuxtLink>
        <NuxtLink :to="route.path + '?hl=en'">EN</NuxtLink>
    </div>
</template>

<script>
export default {
    name: "Languages",
    setup() {
        const route = useRoute()
        return {
            route
        }
    }
}
</script>

<style scoped>
div,
div a {
    height: 40px;
    display: inline-block;
}

img {
    height: 60%;
    display: inline-flex;
    margin: 8px;
}
</style>

languages.js:

function languages(page) {
    let langCookie = useCookie("language")
    let language = langCookie.value
    if (page.$route.query.hl) {
        language = page.$route.query.hl
        langCookie.value = language
    }
    return language || 'az';
}

export default languages

backgroung-image: url(“../../img/main_img.png”) doesn’t work with webpack 5

I have scss folder which contains all scss files and subfolders with another scss files of the project. If I make subfolder in scss folder (home-page or any folder) webpack couldn’t deal with scss files in subfolders and browser doesn’t backgroung-image: url("../../img/main_img.png"). And said that I get an error.

structure:

--node_modules
--src
----scss
------style.scss
------home-page
--------_home-page.scss
----img
------main_img.png
------img-about
--------about_img.png  
--app.js

style.scss

@import "home-page/style-home-page";
@import "reset";

_style-home-page.scss

.home_section {
  backgroung-image: url("../../img/main_img.png"); 
}

app.js

import './scss/style.scss';
import './img/main_img.png';

webpackconfig.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');


// noinspection JSUnusedGlobalSymbols
module.exports = {
    // stats: {
    //     loggingDebug: ["sass-loader"],
    // },
    mode: 'development',
    devtool: 'source-map',
    entry: {
        bundle: path.resolve(__dirname, 'src/app.js')
    },
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "app.[hash:3].js",
        assetModuleFilename: pathData => {
            const filepath = path
                .dirname(pathData.filename)
                .split('/')
                .slice(1).join('/');
            return `${filepath}/[name][ext]`;
        },
        clean: true,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src', 'index.html'),
            filename: "index.html"
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src', 'about.html'),
            filename: "about.html"
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src', 'shop-page.html'),
            filename: "shop-page.html"
        }),
    ],
    module: {
        rules: [
            {
                test: /.(?:js|mjs|cjs)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            ['@babel/preset-env', {targets: "defaults"}]
                        ]
                    }
                }
            },
            {
                test: /.(sa|sc|c)ss$/i,
                use: [
                    'style-loader',
                    {loader: 'css-loader', options: {importLoaders: 2, sourceMap: true}},
                    {loader: 'postcss-loader', options: {sourceMap: true}},
                    {
                        loader: 'sass-loader', options: {
                            implementation: require('sass'),
                            sourceMap: true,
                        }
                    },
                ],
            },
            {test: /.(png|jpg|jpeg|gif|svg)$/i, type: 'asset/resource'},
            {test: /.(woff(2)?|eot|ttf|otf)$/, type: 'asset/inline'},
        ],
    },
    devServer: {
        static: {directory: path.resolve(__dirname, "dist")},
        watchFiles: ["src/*.html"],
        compress: true,
        hot: true,
        port: 9000,
    }
}

How can I resolve this bug? How should I make the path for seeing background-image to the browser?

Performing event on ClassList and Unable to get event.target from ClassList? [duplicate]

I have given btn class separately to all buttons. I want to get all buttons using .getElementsByClassName("btn") I know it return a ClassList. Next I want get a single button on which click event occur so that I wrote below code

let btn = document.getElementsByClassName("btn");
console.log(btn);        // successfully got all buttons in a class list

btn.addEventListener("click",(event)=>{
    
    let target_btn = event.target;
    target_btn.style.boxShadow = "inset 0 -1px 16px 1px black";
    target_btn.style.transition = "all 0.4s";

});

I want to get event occurred button (event.target) from ClassList so that I can add box-shadow & transition to that target button but unable to do.
In console, I also got Uncaught TypeError: btn.addEventListener is not a function

Combination of two Java commands

I want to combine two Java movements according to the sample site
https://rebelan.menew.pw
When the menu becomes active, it will come to center and show the selected div
Also, it should be activated and placed in the center by scrolling on the relevant page.

There are two separate movements that I want to combine

and

tanks

String processing in Javascript using Array

Here is the input that should be converted to JSON format as given below.

'{RS0004036}:{1;2}:{0000003AB;0000003BC}_{RS0004036}:{0;3}:{0000003DE;0000003FG}_{RS0004036}:{3;3}:{0000003HI;0000003JK}'

Target JSON should be like this. The code should read the above input, will create a JSON return string as follows. The returning JSON will return store which will be only one value of the index 0 of the input string and will not repeat as partList.

"storeList": [
                {
                    "store": "RS0004036",
                    "partList": [
                        {
                            "part": "0000003AB",
                            "partSub": "0000003BC",
                            "qtyOnHand": "0",
                            "qtyMinMax": "3"
                        },
                               {
                            "part": "0000003DE",
                            "partSub": "0000003FG",
                            "qtyOnHand": "3",
                            "qtyMinMax": "3"
                        },
                        {
                            "part": "0000003HI",
                            "partSub": "0000003JK",
                            "qtyOnHand": "1",
                            "qtyMinMax": "2"
                        }
                    ]
                }
            ]

I tried the following code:

String.prototype.replaceAll = function replaceAll(search, replace) {
  return this.split(search).join(replace);
}
var storeList = {};
var partList = [];
storeList.partList = partList;
var hdr_str = '{RS0004036}:{1;2}:{0000003AB;0000003BC}_{RS0004036}:{0;3}:{0000003DE;0000003FG}_{RS0004036}:{3;3}:{0000003HI;0000003JK}';
var sites_ar = [];
var str_array = hdr_str.split('_');

var str_us = [];
var qty_ar = [];
var hdr_parts = [];

var part_semi = [];
var part_detail = [];

function populate_part(str_in) {
  for (var i = 0; i < str_array.length; i++) {
    if (str_array[i] !== '"') {
      var str_colon = str_array[i];

      if (str_colon !== '"') {
        if (str_colon !== undefined) {
          const part_detail = str_colon.split(':');
          storeList.store = part_detail[0]
            partList = {
            "part": part_detail[0],
            "qtyOnHand": part_detail[1],
            "qtyMinMax": part_detail[2]
          }
          storeList.partList.push(partList);
        }
      }
    }
  }
}
pattern = ';';
replacement = ',';
hdr_str = hdr_str.replaceAll(pattern, replacement);
pattern = ':';
replacement = ',';
hdr_str = hdr_str.replaceAll(pattern, replacement);
hdr_str = '[' + hdr_str + ']';

for (var i = 0; i < hdr_str.length; i++) {
  if (str_array[i] !== '"') {
    populate_part(str_array[i]);
  }
}
retun(JSON.stringify(storeList));

But it is returning duplicates, not sure where is the issue.

{"partList":[{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{0;3}","qtyMinMax":"{0000003DE;0000003FG}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"},{"part":"{RS0004036}","qtyOnHand":"{1;2}","qtyMinMax":"{0000003AB;0000003BC}"},{"part":"{RS0004036}","qtyOnHand":"{3;3}","qtyMinMax":"{0000003HI;0000003JK}"}],"store":"{RS0004036}"}

Uncaught TypeError: Cannot read properties of undefined (reading ‘ratings’) ?? Pls

I guess what happening is use Effect is trying to load rating or the component before the request is fetched in async await that is too slow and ratings recieved after use Effect runs it says rating is undefined !!!!! idk what i should do now i ve tried so many things. pls help?

My Product.js

import React, { Fragment, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import Pagination from "react-js-pagination";
import "./Products.css";
import { useSelector, useDispatch } from "react-redux";
import { clearErrors, getProduct } from "../../actions/productAction";
import Loader from "../layout/Loader/Loader";
import ProductCard from "../Home/ProductCard";
import Slider from "@material-ui/core/Slider";
import Typography from "@material-ui/core/Typography";
import { useAlert } from "react-alert";
import Metadata from "../layout/Metadata";

const categories = [
  "Laptop",
  "Footwear",
  "Bottom",
  "Tops",
  "Attire",
  "Camera",
  "phone",
];

const Products = () => {
  const dispatch = useDispatch();
  const alert = useAlert();
  const { keyword } = useParams();

  const [currentPage, setCurrentPage] = useState(1);
  const [price, setPrice] = useState([0, 25000]);
  const [category, setCategory] = useState("");
  const [ratings, setRatings] = useState(0);

  const {
    loading,
    error,
    products,
    productsCount,
    resultPerPage,
    filteredProductsCount,
  } = useSelector((state) => state.products);

  // const keyword = match.params.keyword;

  const setCurrentPageNo = (e) => {
    setCurrentPage(e);
  };
  const priceHandler = (event, newPrice) => {
    setPrice(newPrice);
  };
  
  let count = filteredProductsCount;

  useEffect(() => {
    if (error) {
      alert.error(error);
      dispatch(clearErrors());
    }
    dispatch(getProduct(keyword, currentPage, price, category, ratings));
  }, [dispatch, keyword, currentPage, price, category, ratings, alert, error]);

  return (
    <Fragment>
      {loading ? (
        <Loader />
      ) : (
        <Fragment>
          <Metadata title="ECOMMERCE" />
          <h2 className="productsHeading">Products</h2>
          <div className="products">
            {products &&
              products.map((product) => (
                <ProductCard key={product._id} product={product} />
              ))}
          </div>

          <div className="filterBox">
            <Typography>Price</Typography>
            <Slider
              value={price}
              onChange={priceHandler}
              valueLabelDisplay="auto"
              aria-labelledby="range-slider"
              min={0}
              max={25000}
            />

            <Typography>Categories</Typography>
            <ul className="categoryBox">
              {categories.map((category) => (
                <li
                  className="category-link"
                  key={category}
                  onClick={() => setCategory(category)}
                >
                  {category}
                </li>
              ))}
            </ul>

            <fieldset>
              <Typography component="legend">Ratings Above</Typography>
              <Slider
                defaultValue={ratings}
                onChange={(e, newRating) => {
                  setRatings(newRating);
                }}
                aria-labelledby="continuous-slider"
                valueLabelDisplay="auto"
                min={0}
                max={5}
                marks={true}
              />
            </fieldset>
          </div>

          {resultPerPage < count && (
            <div className="paginationBox">
              <Pagination
                activePage={currentPage}
                itemsCountPerPage={resultPerPage}
                totalItemsCount={productsCount}
                onChange={setCurrentPageNo}
                nextPageText="Next"
                prevPageText="Prev"
                firstPageText="1st"
                lastPageText="Last"
                itemClass="page-item"
                linkClass="page-link"
                activeClass="pageItemActive"
                activeLinkClass="pageLinkActive"
              />
            </div>
          )}
        </Fragment>
      )}
    </Fragment>
  );
};

export default Products;

My productAction

import axios from "axios";
import {
  ALL_PRODUCT_REQUEST,
  ALL_PRODUCT_SUCCESS,
  ALL_PRODUCT_FAIL,
  CLEAR_ERRORS,
  PRODUCT_DETAILS_REQUEST,
  PRODUCT_DETAILS_SUCCESS,
  PRODUCT_DETAILS_FAIL,
} from "../constants/productConstants";

export const getProduct =
  (keyword = "", currentPage = 1, price = [0, 25000], category, ratings = 0) =>
  async (dispatch) => {
    try {
      dispatch({ type: ALL_PRODUCT_REQUEST });

      let link = `/api/v1/products?keyword=${keyword}&page=${currentPage}&price[gte]=${price[0]}&price[lte]=${price[1]}&ratings[gte]=${ratings}`;

      if (category) {
        link = `/api/v1/products?keyword=${keyword}&page=${currentPage}&price[gte]=${price[0]}&price[lte]=${price[1]}&category=${category}&ratings[gte]=${ratings}`;
      }

      const { data } = await axios.get(link);
      dispatch({
        type: ALL_PRODUCT_SUCCESS,
        payload: data,
      });
    } catch (error) {
      dispatch({
        type: ALL_PRODUCT_FAIL,
        payload: error.response.data.message,
        
      });
    }
  };

export const getProductDetails = (id) => async (dispatch) => {
  try {
    dispatch({ type: PRODUCT_DETAILS_REQUEST });

    const { data } = await axios.get(`/api/v1/product/${id}`);

    dispatch({
      type: PRODUCT_DETAILS_SUCCESS,
      payload: data.product,
    });
  } catch (error) {
    dispatch({
      type: PRODUCT_DETAILS_FAIL,
      payload: error.response.data.message,
    });
  }
};

//Clearing Errors
export const clearErrors = () => async (dispatch) => {
  dispatch({ type: CLEAR_ERRORS });
};

My productReducer

import {
  ALL_PRODUCT_REQUEST,
  ALL_PRODUCT_SUCCESS,
  ALL_PRODUCT_FAIL,
  CLEAR_ERRORS,
  PRODUCT_DETAILS_REQUEST,
  PRODUCT_DETAILS_SUCCESS,
  PRODUCT_DETAILS_FAIL,
} from "../constants/productConstants";

export const productsReducer = (state = { products: [] }, action) => {
  switch (action.type) {
    case ALL_PRODUCT_REQUEST:
      return {
        loading: true,
        product: [],
      };
    case ALL_PRODUCT_SUCCESS:
      return {
        loading: false,
        products: action.payload.products,
        productsCount: action.payload.productCount,
        resultPerPage: action.payload.resultPerPage,
        filteredProductsCount: action.payload.filteredProductsCount,
      };
    case ALL_PRODUCT_FAIL:
      return {
        loading: false,
        error: action.payload,
      };
      //...state meand access everything present in the state object
    case CLEAR_ERRORS:
      return {
        ...state,
        error: null,
      };
    default:
      return state;
  }
};

export const productsDetailsReducer = (state = { product: {} }, action) => {
  switch (action.type) {
    case PRODUCT_DETAILS_REQUEST:
      return {
        loading: true,
        ...state,
      };
    case PRODUCT_DETAILS_SUCCESS:
      return {
        loading: false,
        product: action.payload,
      };
    case PRODUCT_DETAILS_FAIL:
      return {
        loading: false,
        error: action.payload,
      };
    case CLEAR_ERRORS:
      return {
        ...state,
        error: null,
      };
    default:
      return state;
  }
};