Combining SubArray Properties in a Redux Reducer Array by Matching Objects

I am currently developing a unique keyword generation tool for my application. The tool takes a list of cities and organizes them into keyword groups based on specific categories.

So far, I have experimented with different methods such as using .map on the state and .find to identify matches. However, I am encountering difficulties in properly merging the data to achieve the desired end result. Any guidance or suggestions on how to accomplish this would be greatly appreciated.

const generatedKeywords = (state = [], action) => {
switch (action.type) {
    case types.KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS: {
      return [...state, ...action.payload];
    }
    default: {
      return state;
    }
  }
};

When triggering an action, the input looks something like:

[
   { 
     cityName: "Los Angeles", 
     keywords: ["Los Angeles HVAC Repair", "HVAC Installation Los Angeles"]
   }, 
   {
     cityName: "Sacramento", 
     keywords: ["Sacramento HVAC Repair", "HVAC Installation Sacramento"]
    }
]

The initial action works fine, but subsequent actions do not merge objects with the same cityName. Instead, they produce:

[
    {
        "cityName": "Los Angeles",
        "keywords": [
            [
                "Los Angeles Roofing",
                "Roofers in Los Angeles"
            ]
        ]
    },
    {
        "cityName": "Sacramento",
        "keywords"&: [
            [
                "Sacramento Roofing",
                "Roofers in Sacramento"
            ]
        ]
    },
    ...
]

Desired output is shown below:

[
    {
        "cityName": "Los Angeles",
        "keywords": [            
                "Los Angeles Roofing",
                "Roofers in Los Angeles",
                "HVAC Company in Los Angeles",
                "Los Angeles HVAC Inspection"
        ]
    },
    {
        "cityName": "Sacramento",
        "keywords": [            
                "Sacramento Roofing",
                "Roofers in Sacramento",
                "HVAC Company in Sacramento",
                "Sacramento HVAC Inspection"            
        ]
    }
]

Answer №1

Currently, the process involves concatenating the new state with the old state array. Instead, it would be more efficient to perform a merge operation.

If the state pertains to single entries for specific cities or keywords, using Objects instead of Arrays might simplify the storage. However, this choice largely depends on personal preference.

To demonstrate, consider the following two snippets.

1. State represented as an Array with merge logic in the reducer

  • Create a temporary state containing a Map for all cities and a Set for all keywords.
  • Iterate over the new entries and either merge them with existing state or add them as new entries.
  • Finally, transform the data back into the desired array format.

Note: For smaller states and payloads, utilizing array methods like .includes and .find can help identify duplicate entries for merging.

const types = { KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS: 1 };

const generatedKeywords = (state = [], action = {}) => {
  switch (action.type) {
    case types.KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS: {
      const stateMap = new Map(
        state.map(s => [ s.cityName, new Set(s.keywords) ])
      );
      
      for (const { cityName, keywords } of action.payload) {
        if (stateMap.has(cityName)) {
          const wordSet = stateMap.get(cityName);
          keywords.flat().forEach(word => { wordSet.add(word); });
        } else {
          stateMap.set(cityName, new Set(...keywords));
        }
      }
      
      return Array
        .from(stateMap.entries())
        .map(([ cityName, keywords ]) => ({ cityName, keywords: Array.from(keywords) }));
    }
    default: {
      return state;
    }
  }
};

let state = generatedKeywords();
state = generatedKeywords(state, { type: types.KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS, payload: data1() })
state = generatedKeywords(state, { type: types.KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS, payload: data2() })

console.log(state);

// DATA
function data1() { return [
    {
        "cityName": "Los Angeles",
        "keywords": [
            [
                "Los Angeles Roofing",
                "Roofers in Los Angeles"
            ]
        ]
    },
    {
        "cityName": "Sacramento",
        "keywords": [
            [
                "Sacramento Roofing",
                "Roofers in Sacramento"
            ]
        ]
    }
];}

function data2() { return [
    {
        "cityName": "Los Angeles",
        "keywords": [
            [
                "HVAC Company in Los Angeles",
                "Los Angeles HVAC Inspection"
            ]
        ]
    },
    {
        "cityName": "Sacramento",
        "keywords": [
            [
                "HVAC Company in Sacramento",
                "Sacramento HVAC Inspection"
            ]
        ]
    }
];}

2. State represented as an Object

This state format ensures there is only one entry per city. A helper function is essential to avoid adding duplicate keywords.

  • Spread the old state,
  • Add an entry for each city in the payload,
  • When creating these entries, ensure to merge any existing keywords (state[cityName]?.keywords)

const types = { KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS: 1 };
const uniques = xs => Array.from(new Set(xs));

const generatedKeywords = (state = {}, action = {}) => {
  switch (action.type) {
    case types.KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS: {
      return {
        ...state,
        ...Object.fromEntries(action.payload.map(
          ({ cityName, keywords }) => [ 
            cityName,
            {
              cityName,
              keywords: uniques(
                keywords.flat().concat(
                  state[cityName]?.keywords ?? []
                )
              )
            }
          ]))
      }
    }
    default: {
      return state;
    }
  }
};

let state = generatedKeywords();
state = generatedKeywords(state, { type: types.KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS, payload: data1() })
state = generatedKeywords(state, { type: types.KEYWORD_GENERATOR_TOOL_ADD_GENERATED_KEYWORDS, payload: data2() })

console.log(Object.values(state));

// DATA
function data1() { return [
    {
        "cityName": "Los Angeles",
        "keywords": [
            [
                "Los Angeles Roofing",
                "Roofers in Los Angeles"
            ]
        ]
    },
    {
        "cityName": "Sacramento",
        "keywords": [
            [
                "Sacramento Roofing",
                "Roofers in Sacramento"
            ]
        ]
    }
];}

function data2() { return [
    {
        "cityName": "Los Angeles",
        "keywords": [
            [
                "HVAC Company in Los Angeles",
                "Los Angeles HVAC Inspection"
            ]
        ]
    },
    {
        "cityName": "Sacramento",
        "keywords": [
            [
                "HVAC Company in Sacramento",
                "Sacramento HVAC Inspection"
            ]
        ]
    }
];}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Generating input fields dynamically in a list and extracting text from them: A step-by-step guide

I am designing a form for users to input multiple locations. While I have found a way to add input boxes dynamically, I suspect it may not be the most efficient method. As I am new to this, I want to ensure I am doing things correctly. Here is how I curren ...

Adding data from PHP into a designated div element

update: After some trial and error, I managed to find a solution using jQuery and append() methods. From my PHP code, I created an array containing all the necessary information that I wanted to insert into the tab divs. I then encoded this array into JSON ...

Obtain an NSRange object by using an NSArray

I am working with an NSArray that stores objects in the format {117, 22}{http://t.co/7l3oiMDQt3}. My goal is to extract values like 117 and 22. I initially attempted to use ObjectAtIndex:, but it resulted in a crash. I then implemented the following code s ...

Verify whether a string, or a string split into individual words, includes any words from a given list

My Twitter bot is programmed to skip tweets containing specific blacklisted words. While it currently works correctly, it only blocks tweets with the exact spelling of the blacklisted words. timeline = filter(lambda status: not any(word in status.text.sp ...

Came across some code where I was reading the source and stumbled upon `const {foo} = foo;

I recently encountered the line of code const {foo} = foo in my JavaScript studies. I'm having trouble understanding its meaning despite multiple attempts. Can anyone provide a clear explanation for this please? ...

Prevent click event from bubbling up in React and DOM event mix situations

We are offering a menu here. In case the menu is visible, users should be able to close it by clicking anywhere on the screen: class Menu extends Component { componentWillMount() { document.addEventListener("click", this.handleClickOutside) ...

Encountered an issue while verifying req.params.id in an express.js route

Hi there, I'm reaching out for assistance with my first question on StackOverflow. As a newcomer to full-stack programming, I've encountered an issue while attempting to GET data from a JSON file via a GET Method on my local Node.js Express serve ...

Is there a way to simultaneously click on a link on one page and alter the position of a particular class on another page?

I have been working on designing two pages for a website. I am looking to use JavaScript to ensure that when a link on page 1 is clicked and the user transitions to page 2, the second tab button (btn) will have an id of "default" and receive the activate c ...

Recreating C# array initialization behavior through the power of Reflection

After utilizing ILDasm to analyze some C# code, I discovered that the compiler utilizes the System.Runtime.CompilerServices.RuntimeHelper.InitializeArray method to set up arrays with constant values of primitive types. It seems like a distinct structure i ...

Checking the accuracy of pixels in an image

How can I check the width and height of an image upload in AngularJS? For example, if the width is greater than 200 pixels and the height is greater than 200 pixels, it should show an error. How can I achieve this using AngularJS? HTML <div class="for ...

Tips for effectively utilizing JavaScript regex to precisely match a string before triggering the submit button

I have a form in Angular with a text input field that requires the entry of lowercase letters separated by commas, like this: c, d, e, g, a, f etc... Currently, the submit button activates as soon as any part of the input matches the required format, allo ...

Can Materializecss be utilized without relying on jQuery?

I'm interested in utilizing materializecss without relying on jQuery. Specifically, I would like to achieve the following without the need for jQuery: document.querySelector('.chips-initial').Materialize.Chips.init({ data: [{ tag: ...

There seem to be some issues arising from the Angular Chain HTTP component

I've been working on migrating data from one database to another in an angular.js application, and I am facing some challenges with the code. The specific issue arises when trying to obtain authorization credentials for sending POST requests to the re ...

Struggling to find your way around the header on my website? Let me give

I'm looking to display certain links prominently at the top of a page, similar to this example: "home > page1 > link1 > article 1." Can someone advise on how to achieve this using HTML, PHP, or jQuery? ...

Encountering an error message stating, "Unable to interpret property

I need to retrieve specific information import React,{Component} from 'react'; import axios from 'axios'; import CoinsConvert from '../data/data' import '../style/bootstrap.min.css'; class BoxInfo extends Component ...

Maintain cookie data between pages to remember form inputs

The scenario: A form is utilized to capture the input value using $thisSearch.val(), store it as a cookie, and then add it to an array. This form is displayed as an overlay triggered from a menu item in the website header, allowing it to be accessed from a ...

Chart.js is able to read JSON files offline, but it encounters difficulties reading them online

I am encountering an issue with my chart.js. When I attempt to read an offline JSON file, the chart appears as expected. However, when using an online JSON file from Firebase, it only displays a white screen. I have exported a JSON file from Firebase and ...

Transforming the String Values in an Array to Capitalize the First Letter of Each Word Using VueJS

After receiving an array of objects from an API call: "data": [ { "heading_one_of_the_table": 14, "total": 8 }, { "heading_one_of_the_table": 1, "total": 7 }, { "heading_one_of_the_table": 6, "total": 7 } ] I want to ...

What could be causing my div element to remain stationary despite the movement functions I have implemented?

I am attempting to move my div element by using key presses to adjust the CSS attributes of the HTML elements. However, despite implementing the necessary code, the mover refuses to budge and nothing appears when I inspect the elements or their attributes. ...

Preventing preventDefault() from firing in JQuery only when necessary

I have come up with a script that I recently created. <script> $(document).ready(function(){ $('a').data('loop',true); $('body').on('click', 'a', function(event){ console.lo ...