What is the most efficient way to organize a list within an object using functional programming, taking into account varying criteria for sorting depending on

In this scenario, we have an 'api_fetchData' function that retrieves data from the server. Depending on the route, the data can be in the form of a table or a tree structure. There are two states that need to be updated based on the received data. Additionally, if the data is in table format, the records should be sorted by priority. I am exploring how to achieve this using functional programming techniques, perhaps using Ramda or other similar libraries.

 const tree=null;

and

const table = null:

//table

 {
        type: "table",
        records: [
          {
            id: 1,
            priority: 15
          },
          {
            id: 2,
            priority: 3
          }
        ]
}

//tree

{
        type: "tree",
        children: [
          {
            type: "table",
            records: [
              {
                id: 1,
                priority: 15
              },
              {
                id: 2,
                priority: 3
              }
            ]
          }
        ]
  }

This is the approach I took:

//define utils

  const Right = x => ({
    map: f => Right(f(x)),
    fold: (f, g) => g(x),
    inspect: x => `Right(${x})`
  });
  const Left = x => ({
    map: f => Left(x),
    fold: (f, g) => f(x),
    inspect: x => `Left(${x})`
  });
  const fromNullable = x => (x != null ? Right(x) : Left(null));
  const state = reactive({
    path: context.root.$route.path,
    tree: null,
    table: null
  });

// final code:

  const fetchData = () => {
    return fromNullable(mocked_firewall[state.path])
      .map(data =>
        data.type === "tree"
          ? (state.tree = data)
          : (state.table = R.mergeRight(data, {
              records: prioritySort(data.records)
            }))
      )
      .fold(
        () => console.log("there is no data based on selected route"),
        x => console.log(x)
      );
  };

Answer №1

You are facing several challenges in this scenario -

  1. Effectively accessing deeply nested data in an uncertain object
  2. Organizing an array of intricate data using functional methods
  3. Safely and unchangeably modifying deeply nested data in an uncertain object

1. The use of .map and .chain may seem rudimentary, additional levels of abstraction should be incorporated as the syntax becomes cumbersome –

const state =
  { tree:
      { type: "sequoia"
      , children: [ "a", "b", "c" ]
      }
  }

recSafeProp(state, [ "tree", "type" ])
  .getOrElse("?") // "sequoia"

recSafeProp(state, [ "tree", "foobar" ])
  .getOrElse("?") // "?"

recSafeProp(state, [ "tree", "children", 0 ])
  .getOrElse("?") // "a"

recSafeProp(state, [ "tree", "children", 1 ])
  .getOrElse("?") // "b"

recSafeProp(state, [ "tree", "children", 99 ])
  .getOrElse("?") // "?"

We can easily implement recSafeProp by creating our own utility function, safeProp, in the process –

const { Nothing, fromNullable } =
  require("data.maybe")

const recSafeProp = (o = {}, props = []) =>
  props.reduce // for each props as p
    ( (r, p) => // safely lookup p on child
        r.chain(child => safeProp(child, p))
    , fromNullable(o) // init with Maybe o
    )

const safeProp = (o = {}, p = "") =>
  Object(o) === o // if o is an object
    ? fromNullable(o[p]) // wrap o[p] Maybe
    : Nothing()  // o is not an object, return Nothing

2. The Comparison module. Given your familiarity with functional modules, let's delve into it –

const { empty, map } =
  Comparison

const prioritySort =
  map(empty, record => record.priority || 0)

// or...
const prioritySort =
  map(empty, ({ priority = 0}) => priority)

myarr.sort(prioritySort)

If you require an immutable sort –

const isort = ([ ...copy ], compare = empty) =>
  copy.sort(compare)

const newArr =
  isort(origArr, proritySort)

Here's the Comparison module. It is introduced in this Q&A. Refer to it there to see how to create complex sorters in a functional manner –

const Comparison =
  { empty: (a, b) =>
      a < b ? -1
        : a > b ? 1
          : 0
  , map: (m, f) =>
      (a, b) => m(f(a), f(b))
  , concat: (m, n) =>
      (a, b) => Ordered.concat(m(a, b), n(a, b))
  , reverse: (m) =>
      (a, b) => m(b, a)
  , nsort: (...m) =>
      m.reduce(Comparison.concat, Comparison.empty)
  }

const Ordered =
  { empty: 0
  , concat: (a, b) =>
      a === 0 ? b : a
  }

Combining sorters –

const { empty, map, concat } =
  Comparison

const sortByProp = (prop = "") =>
  map(empty, (o = {}) => o[prop])

const sortByFullName =
  concat
    ( sortByProp("lastName")  // primary: sort by obj.lastName
    , sortByProp("firstName") // secondary: sort by obj.firstName
    )

data.sort(sortByFullName) // ...

Composable sorters –

const { ..., reverse } =
  Comparison

// sort by `name` then reverse sort by `age`&nbsp;&ndash;
data.sort(concat(sortByName, reverse(sortByAge)))

Functional prinicples –

// this...
concat(reverse(sortByName), reverse(sortByAge))

// is the same as...
reverse(concat(sortByName, sortByAge))

As well as –

const { ..., nsort } =
  Comparison

// this...
concat(sortByYear, concat(sortByMonth, sortByDay))

// is the same as...
concat(concat(sortByYear, sortByMonth), sortByDay)

// is the same as...
nsort(sortByYear, sortByMonth, sortByDay)

3. The easiest solution to consider in this situation is Immutable.js. If time permits, I will demonstrate a low-fi approach to this specific problem later on.

Answer №2

It appears to me that your solution may be a bit excessive.

Functional programming encompasses various concepts, and while there isn't a concise definition, focusing on pure functions and avoiding data mutation is a key aspect of being a functional programmer. While the Either monad can offer powerful advantages in certain situations, it seems like in this code, there is an attempt to force the use of Either where it may not be the best fit.

Here is a suggestion on how to approach this code. I made some assumptions along the way, such as the need for an asynchronous process when fetching data from a server, using methods like Promises, async-await, or similar alternatives such as Tasks or Futures. Additionally, I assumed that the part where you reference mocked_firewall involves the actual asynchronous call. Although your sample code treated it as an object for looking up results from a path, the logic was a bit unclear regarding its resemblance to a real service. Lastly, the use of

fold(() => log(...), x => log(x))
was merely for demonstration purposes to showcase the code's functionality.

With these considerations in mind, I suggested a version with an object mapping type to functions, each taking data and state as inputs and returning a new state. The central function, fetchData, takes something akin to your mocked_firewall (or a modified version to return a Promise) and returns a function that accepts a state and returns a new state.

The revised code snippet looks as follows:

Answer №3

🎈 Here's a concise approach:

My approach involves using ramda, where you can combine functions to achieve the desired result ✨:

const prioritySort = R.compose(
  R.when(
    R.propEq('type', 'tree'),
    R.over(
      R.lensProp('children'),
      R.map(child => prioritySort(child))
    )
  ),
  R.when(
    R.propEq('type', 'table'),
    R.over(
      R.lensProp('records'),
      R.sortBy(R.prop('priority')),
    )
  ),
)

const fetchData = R.pipe(
  endpoint => fetch(`https://your-api.com/${endpoint}`, opts).then(res => res.json()),
  R.andThen(prioritySort),
)

fetchData(`tree`).then(console.log)
fetchData(`table`).then(console.log)

Explore the demo

You can easily inspect the process using the function

const log = value => console.log(value) || value

R.pipe(
  // some code
  log,
  // some code
)

It will display the value as it passes through the pipe.

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

Is there a way to exchange the chosen options between two bootstrap-vue multiple select boxes?

Is there a way to switch the selected options between two bootstrap-vue multiple select boxes? I am trying to create a UI similar to the image shown below but I am struggling with writing the method. This is how I have written the template: https://i.sst ...

Sending JSON Data from Angular2 Component to Node.js Server

Currently, I am facing an issue where I am unable to successfully insert data into a database using Angular2 and Node.js. Upon running my script, I use console.log(this.address); to verify that I am passing json, and the output in the console is as follow ...

Modify the icon used for pagination in Semantic UI

I'm currently utilizing the Pagination feature from Semantic UI and I'm curious if there is a way to customize the icons for the next/previous buttons. Below is the code for the component: import React from 'react' import { Pagination ...

Every time I attempt to run "npm install" on Visual Studio Code, I encounter an error

Every time I try to run npm install, I encounter this error. My node version is 18.9.1 and I have exhausted all possible solutions. Any help would be greatly appreciated. '''npm ERR! code ENOENT npm ERR! syscall open npm ERR! path C:\Us ...

One particular item in the list does not conform to the same formatting as the others

Having a formatting issue with the indexLink when populating an unordered list with links. Can't seem to dedent it properly no matter what I try. Here's the javascript snippet: function addCrumb() { /* reads from localStorage and determines wh ...

Verify whether a dependency is being passed into an Angular component

As mentioned in a discussion about IE caching issues, Internet Explorer tends to cache things when using ajax with AngularJS, causing potential problems. You can find a helpful solution to this problem here. Given that we have multiple angular apps on our ...

Turn off integrity verification for local dependencies in package-lock.json

Is there a way to bypass the integrity check for a local dependency in package-lock.json? Within my project repository, I have a core library along with two Angular applications that both rely on this core library as a dependency. The problem arises beca ...

What is the best way to create a seamless Asynchronous loop?

Adhering to the traditional REST standards, I have divided my resources into separate endpoints and calls. The primary focus here revolves around two main objects: List and Item (with a list containing items as well as additional associated data). For ins ...

The NestJS error code 413 indicates that the payload size is too large for

I am currently utilizing nestjs and have encountered an issue where an endpoint only accepts files via multipart. When attempting to upload files that are less than 10MB, everything works perfectly fine. However, when trying to upload files larger than 10M ...

Using AngularJS to bind objects with a checkbox

I am dealing with a nested list of objects that can go up to three levels deep. Each object contains another nested object, and this nesting can continue to multiple levels. I am interested in binding these objects directly to checkboxes so that when I che ...

Guide to setting up parameterized routes in GatsbyJS

I am looking to implement a route in my Gatsby-generated website that uses a slug as a parameter. Specifically, I have a collection of projects located at the route /projects/<slug>. Typically, when using React Router, I would define a route like t ...

The most efficient method for handling a vast amount of data in NodeJS

My database consists of 4 million numbers and I need to quickly check if a specific number exists in it. Example of the database: [177,219,245,309,348,436,...] I initially tried using a MySQL table for this task, but it took a lengthy 1300ms just to chec ...

The property is returning an empty string, but the function is functioning perfectly

Check out this related Stack Overflow post exports.getAddress = asyncHandler(async (req, res, next) => { var lon = req.query.lon; var lat = req.query.lat; var formattedAddress = ""; var url1 = 'url' request(url1 ...

Steps for adjusting the matMenuTriggerFor area so it only triggers when hovering over the arrow

Hello there! I'm currently working on adjusting the trigger area for opening the next menu panel. Right now, the next menu panel opens whenever I hover over either the title or the arrow. However, my goal is to have the menu open only when I hover ove ...

Webpack does not support d3-tip in its current configuration

I'm having some trouble getting d3-tip to work with webpack while using TypeScript. Whenever I try to trigger mouseover events, I get an error saying "Uncaught TypeError: Cannot read property 'target' of null". This issue arises because th ...

"Uncovering the mystery of the missing element in Jquery's open

I have developed a click-to-show feature that opens and closes a div from left to right. You can view the demo here on jsfiddle.net In the demo, you will find a green-colored div. Upon clicking this div, another div opens from left to right. An interesti ...

Comparing the previously selected node with the currently selected node in an ASP.NET treeview

I am looking to implement a comparison between the last selected node and the currently selected node on a tree view using JavaScript. Can anyone provide me with some code snippets to help me compare the previous selection with the current selection on th ...

Lazy Load immediately loads images that are visible on the screen without needing a click

I am facing an issue with Lazy Load on my image-heavy website. I want the images to load only when a button is clicked, but currently, it only partially works. Images below the fold follow the desired behavior of loading on click, but those above the fold ...

The website appears to be loading in a unique way on mobile upon the second loading

While developing my personal website, I encountered a bug that occurs when accessing the site on my Android phone using Firefox or Chrome. The issue arises when the page initially loads correctly, but upon refreshing, the layout is displayed differently. ...

Create dual modules within a single project

I am working on a mean-stack project. In my index.js, at the end, I have: router.get('*', function(req, res) { res.sendfile('./views/index.html'); }) module.exports = router; Now, I need to handle all webpages that match https://l ...