Merging lists based on their absolute index in functional programming

I am searching for a simpler approach to combine a dictionary and a list into a single list using functional programming. Here is an example of the dictionary, list, and expected outcome:

const myDictionary = new Map<string, number>([
  ['a', 4],
  ['b', 0],
  ['c', 3]
])

const myList = [
  'a',
  'b',
  'c'
]

const expectedResult = [
  [
    {'index': 0, 'payload': 'a0'},
    {'index': 1, 'payload': 'a1'},
    {'index': 2, 'payload': 'a2'},
    {'index': 3, 'payload': 'a3'}
  ],
  [],
  [
    {'index': 4, 'payload': 'c0'},
    {'index': 5, 'payload': 'c1'},
    {'index': 6, 'payload': 'c2'}
  ]
] 

The number of items in the inner list of expectedResult varies. In this instance, it corresponds to the values in the map myDictionary, but this can vary. The function for generating these inner list items is deterministic, but the values need to be computed, matched, or combined.

My query is how can I accomplish this in a functional manner. I could attempt something like this (using Randa library):

Define a function to transform each list item:

const transformListItem = (char: string, offset: number, dictionary: Map<string, number>) =>
  range(0, dictionary.get(char)).map(i => ({
    'index': i + offset,
    'payload': `${char}${i}`
  }))

Here is how it would be implemented:

const transformList = (list: string[], dictionary: Map<string, number>) =>

    list.map((char, listItemIndex) => {

        const listPriorToThisItem = slice(0, listItemIndex, list)
        const transformedListPriorThisItem = transformList(listPriorToThisItem, dictionary)
        const indexOffset = sum(transformedListPriorThisItem.map(a => a.length))

        return transformListItem(char, indexOffset, dictionary)
    }

const result = transformList(myList, myDictionary)

However, this approach involves redundant calculations for each list item. So, my question is: Is there a way to avoid repeating this calculation multiple times? Memoization is an option, but it adds complexity. A non-functional solution would involve iterating an index variable:


const transformList = (list: string[], dictionary: Map<string, number>) => {

  let indexOffset = 0

  return list.map(char => {
    const transformedItem = transformListItem(char, indexOffset, dictionary)
    indexOffset += transformedItem.length
    return transformedItem
  }
}

const result = transformList(myList, myDictionary)

Are there any more direct patterns in functional programming to achieve this?

Answer №1

If you utilize the mapAccum function, it empowers you to map the items while preserving the index for the subsequent item:

const { range, mapAccum } = R

const transformListItem = (char, offset, dictionary) =>
  range(0, dictionary.get(char)).map(i => ({
    'index': i + offset,
    'payload': `${char}${i}`
  }))
  
const transformList = (list, dictionary) =>
  mapAccum(
    (index, char) => [
      index + dictionary.get(char),
      transformListItem(char, index, dictionary)
    ],
    0, 
    list
  )[1]

const myDictionary = new Map([['a', 4], ['b', 0], ['c', 3]])
const myList = ['a', 'b', 'c']

const result = transformList(myList, myDictionary)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.29.1/ramda.min.js" integrity="sha512-PVSAmDFNqey8GMII8s9rjjkCFvUgzfsfi8FCRxQa3TkPZfdjCIUM+6eAcHIFrLfW5CTFAwAYS4pzl7FFC/KE7Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Answer №2

Understanding how to factor in the declaration of const myList = ['a', 'b', 'c'] is not crucial for achieving the expected result.

If you are dealing with a similar scenario, recursion could be a valuable tool. Utilize Array#from to convert other data types into arrays effectively. The parameter cursor plays a significant role in accumulating the index value throughout the iterations.

const toList = (dict) => {
  
  const worker = (list, cursor = 0) => {
    if(!list.length) {
      return [];
    }
    
    const [head, ...tail] = list;
    const [char, length] = head;
    
    const item = Array.from({ length }, (_, i) => {
      const index = i + cursor;
      const payload = `${char}${i}`
    
      return { index, payload };
    });


    return [item].concat(
      worker(tail, length + cursor),
    );
  };
  
  return worker(
    Array.from(dict),
  );
};


const dict = new Map([
  ['a', 4],
  ['b', 0],
  ['c', 3]
])

console.log(
  toList(dict),
);

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

The absence of a 'defaultValue' argument in Typescript React is causing an issue

I'm encountering an issue with my code. The error message states "An argument for 'defaultValue' was not provided." I am trying to set default values but unsure of what those values should be. Additionally, I am facing a couple of other err ...

What is the source of the efficiency improvement in map or list comprehension implementations compared to invoking a function within a loop?

Exploring the memory efficiency and speed improvements of using map versus loops in function iteration. Is there a specific memory optimization that contributes to the speed boost when utilizing the map function instead of looping? The concept of contiguou ...

What's the best way in Angular 6 to set focus on an element that's being made content editable?

I am currently utilizing the contentEditable attribute in Angular 6 to allow for editing the content of elements within an ngFor loop. Is there a way to focus on a tag element when its contentEditable attribute is set to true? <div class="tag" *ngFor= ...

In Angular 8, a communication service facilitates interaction between parents and children

Using a Sharing service, I am able to pass data from my app component to the router-outlet render component. While I have been successful in passing strings and other data, I am now looking for a way to retrieve data from an API and share it with a compone ...

An error occurred while trying to set the property 'IS_CHECK' of an object that is undefined

I'm attempting to create a checkbox that, when selected, should also select everything else. I followed the code example provided in this tutorial for angular 2. However, I encountered an error: "ERROR TypeError: Cannot set property 'IS_CHECK&ap ...

Limitations on quantity utilizing typescript

Looking to create a type/interface with generics that has two properties: interface Response<T> { status: number; data: T | undefined; } Specifically, I want to enforce a rule where if the status is not equal to 200, then data must be undefined. ...

What is the process for incorporating TypeScript typings into a snippet of jQuery code designed for a Bootstrap multilevel dropdown menu?

I'm exploring Angular for the first time and trying to create a multi-level dropdown using Bootstrap. I came across this article which contains some JavaScript code snippets that I am struggling with. I need help converting this code into TypeScript, ...

Is it recommended to keep Angular properties private and only access them through methods?

I'm starting to get a bit confused with Angular/Typescripting. I originally believed that properties should be kept private to prevent external alteration of their values. Take this example: export class Foo{ private _bar: string; constructor(pr ...

Is it possible to customize error messages in @hapi/joi?

Seeking assistance with custom error message overrides in Joi. Consider the schema outlined below. const joiSchema = Joi.object({ name: Joi.string().required(), email: Joi.string().email().required() }) try{ const schema = joiSchema.validateAsyn ...

Exploring the contrasts and practical applications of Virtual Scroll versus Infinite Scroll within the framework of Ionic 3

After thoroughly reviewing the documentation for Ionic 3, I embarked on a quest to discern the disparity between https://ionicframework.com/docs/api/components/virtual-scroll/VirtualScroll/ and https://ionicframework.com/docs/api/components/infinite-scr ...

Eliminate the hashtag (#) from the URL in Angular 11

I am facing an issue with removing the # from the URL. When I try to remove it, the application encounters a problem upon deployment to the server. Upon page refresh, a 404 error status is returned. For instance: https://example.com/user/1 (works) https ...

Tips for extracting a specific key from a Json api response, verifying the presence of additional keys, and adding them to a list if they exist

Dealing with a complicated issue here - I have a JSON API response that contains the key 'key' nested in 'persons' In some instances, there are multiple occurrences of 'key', and when I attempt to iterate through the response ...

Group multiple typescript files into separate outFile modules

Can TypeScript files be grouped into multiple outFiles? I want to bundle my Typescript code, but instead of one single JS file, I would like to organize my TS into separate JS files such as controllers.js and plugins.js. The options in the TypeScript pro ...

Angular Typescript filter function returning an empty arrayIn an Angular project, a

I have a filtering function in Angular that is returning an empty array. Despite trying similar solutions from previous questions, the issue persists. The function itself appears to be correct. Any assistance would be greatly appreciated. gifts represents ...

Error: TypeScript React Google Maps: The object 'google' is not recognized

After successfully loading the Google Maps JavaScript API in the public/index.html, I can log window.google.maps without any issues in the dev tools console. https://i.sstatic.net/XqeG5.png However, TypeScript seems unaware of its presence, resulting in ...

Triggering createEffect in SolidJS with an external dependency: A guide

Is there a way to use an external dependency to trigger the createEffect function in Solid, similar to React's useEffect dependency array? I am trying to execute setShowMenu when there is a change in location.pathname. const location = useLocation() ...

Assigning to a constrained type with an indexable signature results in failure

When using typescript 4.7.2, I encountered an issue where the following code fails only when assigning a value: type IndexableByString = { [k: string]: any }; function test<T extends IndexableByString>(target: T, key: string) { var prop = target ...

The overload functionality in Typescript interfaces is not functioning as intended

Here is a snippet of code I'm working with: class A { } class B { } class C { } interface Builder { build(paramOne: A): string; build(paramOne: B, paramTwo: C): number; } class Test implements Builder { build(a: A) { return &apo ...

Exploring TypeScript Object Properties in Angular 2

How can I extract and display the ID and title of the Hero object below? The structure of the Hero interface is based on a Firebase JSON response. hero.component.ts import {Component, Input} from 'angular2/core'; import {Hero} from '../mod ...

Exploring Angular 2: Unlocking the Power of Directives within Components

To display a dialog component on the main component page after clicking a button, I used directives in the following way: Within the template: <button id="goToTasksCases" class="btn btn-success btn-lg" (click)="doShowStartNewCase($event)">START A N ...