Juste pour clarifier ceci, c'est ce que j'entends par "carte inversée":

const foo =
  { "a": 10
  , "b": 20
  };

const foo_inverted =
  { "10": "a"
  , "20": "b"
  };

J'ai cet objet représentant un fichier:

const file =
  { id: 100
  , tags: [20, 30]
  };

Étant donné une liste de fichiers, j'ai besoin de créer une carte qui me permet de trouver tous les fichiers avec une balise donnée.

De ceci:

const files =
  [ { id: 100
    , tags: [20, 30]
    }
  , { id: 200
    , tags: [20, 40]
    }
  ];

Pour que:

{ "20": { "100": 1, "200": 1 }
, "30": { "100": 1 }
, "40": { "200": 1 }
}

Je me suis retrouvé avec ce code qui fait le travail:

const tag_file = (tag_id, file_id) => ({[tag_id]: {[file_id]: 1}});
const mergeDeepAll = reduce(mergeDeepRight, {});
const tag_map = compose(mergeDeepAll, lift(tag_file));
const tags_map = compose(mergeDeepAll, map(({id, tags}) => tag_map(tags, [id])));

tags_map(files);
//=> { "20": { "100": 1, "200": 1 }
//=> , "30": { "100": 1 }
//=> , "40": { "200": 1 }
//=> }

Question: est-ce qu'il me manque des concepts de programmation fonctionnelle qui m'auraient permis de mieux exprimer cela?

1
customcommander 17 avril 2020 à 20:04

2 réponses

Meilleure réponse

Créez une fonction qui génère des paires [tag, id] pour chaque objet, en utilisant un Array.map() (idByTags). L'utilisation de R.chain convertit tous les objets en de telles paires et les aplatit. Regroupez par la balise (R.head), puis mappez l'objet (R.mapObjIndexed) et comptez par l'id (R.last):

const { pipe, chain, groupBy, head, mapObjIndexed, countBy, last } = R

const idByTags = ({ id, tags }) => tags.map(tag => [tag, id])

const fn = pipe(
  chain(idByTags),
  groupBy(head),
  mapObjIndexed(countBy(last))
)

const files = [{"id":100,"tags":[20,30]},{"id":200,"tags":[20,40]}]

const result = fn(files)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
2
Ori Drori 17 avril 2020 à 17:21

Je ne sais pas pourquoi vous auriez besoin de ramda, vous pouvez le faire avec réduire et pour chaque

const files = [{
  id: 100,
  tags: [20, 30]
}, {
  id: 200,
  tags: [20, 40]
}];

// loop over the array to make an object
const result = files.reduce((obj, file) => {
  // loop over the tags
  file.tags.forEach(
    tag =>
    obj[tag] ?  // have we seen the tag?
    obj[tag].push(file.id) :  // yes
    obj[tag] = [file.id] // no
  )
  return obj // return the object for reduce
}, {})

console.log(result)

APRÈS VOTRE MODIFICATION

const files = [{
  id: 100,
  tags: [20, 30]
}, {
  id: 200,
  tags: [20, 40]
}];

// loop over the array to make an object
const result = files.reduce((obj, file) => {
  // loop over the tags
  file.tags.forEach(
    tag => {
    obj[tag] = obj[tag] || {}  // have we seen the tag?
    obj[tag][file.id] = 1 //
  })
  return obj // return the object for reduce
}, {})

console.log(result)
0
epascarello 17 avril 2020 à 19:20