4
votes

Comment puis-je mapper un tableau d'objets à un tableau avec un identificateur pour les valeurs en double?

Travailler avec un tableau d'objets comme:

[
  ["Car Wash Drops (0)", 400],
  ["Personal/Seeding (1)", 48],
  ["Personal/Seeding (2)", 48],
]

Je souhaite mapper vers un tableau avec un identifiant supplémentaire pour les valeurs en double:

data.map((d, i) => [`${d.value} ${i}`, d.count]);

Jusqu'à présent, j'ai une fonction de carte pour mapper les valeurs en conséquence, mais je ne sais pas comment procéder pour ajouter l'identifiant uniquement pour les doublons.

[
  ["Car Wash Drops", 400],
  ["Personal/Seeding", 48],
  ["Personal/Seeding", 48],
]

donne:

data.map(d => [`${d.value}`, d.count]);

J'ai également utilisé l'index avec, mais il ajoute l'index sur chaque valeur:

const expected = [
  ["Car Wash Drops", 400],
  ["Personal/Seeding (1)", 48],
  ["Personal/Seeding (2)", 48],
];


3 commentaires

Je suggère de préciser que vous voulez l'index du tableau d'origine pour les éléments dupliqués, pas un compteur avec le nombre de fois que l'élément est présent.


Cette réponse peut être utile: stackoverflow.com/a/36744732/2266428 , car cette question a déjà été posée dans le passé et a de nombreuses réponses.


Double possible de Supprimer les doublons d'un tableau d'objets dans JavaScript


4 Réponses :


-1
votes

Vous pouvez passer un deuxième argument à .map () après le rappel. Cette valeur sera utilisée comme valeur de this lorsque le rappel est appelé. Ainsi, vous pouvez passer par un objet que vous pouvez utiliser pour accumuler un nombre de répétitions:

data.map(function(d, i) {
  if (!(d.value in this))
    this[d.value] = 0;
  else
    this[d.value] += 1;

  return [
    d.value + (this[d.value] ? " (" + this[d.value] + ")" : ""),
    d.count
  ];
}, {});

En commençant par un objet vide, le rappel peut garder une trace du nombre de fois où il est vu chaque chaîne d.value . Lorsqu'il voit une répétition, il peut ajouter le qualificatif à la chaîne.

Maintenant, cela ne fait pas exactement ce que vous avez demandé, car il ne regarde qu'une seule valeur à un temps. Ainsi, la première fois qu'il voit "Personal / Seeding" , il ne sait pas que c'est un doublon, donc il n'est pas modifié avec le qualificatif. La deuxième fois, c'est bien sûr.

Faire ce que vous avez demandé, si cela ne suffit pas, nécessiterait d'abord un passage complet dans le tableau pour obtenir un décompte final des doublons pour chaque chaîne.


0 commentaires

0
votes

Vous pouvez gérer une correspondance entre les valeurs que vous avez vues et le nombre d'entre elles que vous avez vues. Ensuite, il sera plus facile d'obtenir le bon numéro en fonction de la présence ou non d'un doublon:

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
];

let valueCounts = data.reduce((a, c) => {
  a[c.value] = a[c.value] || {current: 1, total: 0};
  a[c.value].total += 1;
  return a;
}, {});

const expected = data.map(({count, value}) => {
  if (valueCounts[value].total === 1) return [value, count];
  return [`${value} (${valueCounts[value].current++})`, count];
});

console.log(expected);


0 commentaires

3
votes

En utilisant votre approche, vous pouvez utiliser filter () à l'intérieur de la carte pour vérifier combien d'éléments sur le tableau d'origine ont la même valeur que celui analysé en cours, en utilisant cette condition, vous pouvez choisir quoi renvoie comme nouvelle valeur:

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
  {count: 300, value: "Operators/Management"},
  {count: 48, value: "Personal/Seeding"}
];

let counters = data.reduce((res, {value}) =>
{
    res.set(value, res.has(value) ? res.get(value) + 1 : 1);
    return res;
}, new Map());

let res = data.map((x, idx) =>
{
    return [
        `${x.value}` + (counters.get(x.value) > 1 ? `(${idx})` : ""),
        x.count
    ]; 
});

console.log(res);

Les performances de l'approche précédente pourraient être améliorées si nous utilisons some () au lieu de filter () , comme ceci:

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
  {count: 300, value: "Operators/Management"},
  {count: 48, value: "Personal/Seeding"}
];

let res = data.map((x, idx) =>
{
    if (data.some((y, j) => y.value === x.value && idx !== j))
        return [`${x.value} (${idx})`, x.count];
    else
        return [`${x.value}`, x.count];
});

console.log(res);

Et pourrait être amélioré encore plus si nous créons auparavant un Carte avec le compteur de fois qu'un élément apparaît dans le tableau d'origine. Comme ceci:

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
];

let res = data.map((x, idx) =>
{
    if (data.filter(y => y.value === x.value).length > 1)
        return [`${x.value} (${idx})`, x.count];
    else
        return [`${x.value}`, x.count];
});

console.log(res);


1 commentaires

Il convient de noter qu'il s'agit d'un processus O (n ^ 2), donc cela coûterait cher si le tableau était beaucoup plus long.



3
votes

La réponse a déjà été donnée, avec tout le respect que je dois à l'auteur de la réponse, je pense que certaines améliorations pourraient être apportées à la réponse, à la fois syntaxiquement et en termes de performances:

  1. réduire doit être évité si la source est très grande, bien que je préfère toujours réduire si la source est relativement petite (data.length POF (purement ancien pour :)) car c'est l'option la plus rapide.

  2. La carte ES6 est une aide précieuse lorsque nous traitons des paires clé-valeur , mais je préfère POO (simple objet ancien :) ) si c'est le cas possible et cela rendra notre code plus esthétique.

Je vais fournir ma solution au problème (il exécutera O (n) et utilisera un espace O (n) supplémentaire pour les paires clé-valeur dans le pire des cas, il fera deux passes sur la source donc , le deuxième passage est nécessaire pour mettre 1 là où nous avons manqué lors de notre première rencontre):

let data =  [
      {count: 400, value: "Car Wash Drops"},
      {count: 48, value: "Personal/Seeding"},
      {count: 48, value: "Personal/Seeding"},
      {count: 300, value: "Operators/Management"},
      {count: 48, value: "Personal/Seeding"}
    ];

    const map = {};

    for (let d of data) {
      map[d.value] = map[d.value]+1 || 0;
      d.value = map[d.value]?d.value+` (${map[d.value]+1})`:d.value; 
    }

    for (let d of data) {
      d.value = map[d.value]?d.value+` (1)`:d.value; 
    }

    console.log(data.map(o=>[o.value,o.count]));

Ou une version plus concise utilisant l'opérateur ES6 of [lien vers de] :

let data =  [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
  {count: 300, value: "Operators/Management"},
  {count: 48, value: "Personal/Seeding"}
];

const map = {};

for (let i=0;i<data.length;i+=1) {
  map[ data[i].value ] = map[ data[i].value ]+1 || 0;
  data[i].value = map[data[i].value]?data[i].value+` (${map[data[i].value]+1})`:data[i].value; 
}

for (let i=0;i<data.length;i+=1) {
  data[i].value = map[data[i].value]?data[i].value+` (1)`:data[i].value; 
}

console.log(data.map(o=>[o.value,o.count]));


0 commentaires