J'ai trois tableaux différents d'objets que je dois trier sur un champ Date , où le champ a un nom différent dans chaque groupe.
Ci-dessous un exemple de mes données:
const result = [
{
conversationId: '5622',
lastUpdate: '2019-08-16T18:19:12+01:00'
},
{
invoiceId: "20100392077",
rechnungsDatum: "2019-02-10"
},
{
invoiceId: "3336392077",
rechnungsDatum: "2018-12-11"
},
{
documentId: '121212',
fileName: 'test3_2018.pdf',
date': '2018-05-22T23:08:52+01:00'
}
]
OBJECTIF: renvoie les 4 derniers résultats (ce que je peux obtenir avec un . reverse () une fois que j'ai un ensemble final répertorié ASC ), indépendamment de la source d'où ils proviennent. Je m'attendrais à obtenir:
const documents = [
{
documentId: 'ADB0125A',
fileName: 'test_2018.pdf',
date': '2017-12-02T19:08:52+01:00' // Field to sort by
},
{
documentId: '123456',
fileName: 'test2_2018.pdf',
date': '2017-12-12T22:08:52+01:00' // Field to sort by
},
{
documentId: '121212',
fileName: 'test3_2018.pdf',
date': '2018-05-22T23:08:52+01:00' // Field to sort by
}];
const conversations = [
{
conversationId: '1102',
lastUpdate: '2015-10-10T18:19:12+01:00' // Field to sort by
},
{
conversationId: '5622',
lastUpdate: '2019-08-16T18:19:12+01:00' // Field to sort by
},
{
conversationId: '112',
lastUpdate: '2015-10-26T18:19:12+01:00' // Field to sort by
}];
const invoices = [
{
invoiceId: "20100392077",
rechnungsDatum: "2019-02-10" // Field to sort by
},
{
invoiceId: "5550392077",
rechnungsDatum: "2018-02-05" // Field to sort by
},
{
invoiceId: "3336392077",
rechnungsDatum: "2018-12-11" // Field to sort by
}];
Est-il possible d'utiliser une méthode unique Array.sort pour les trois objets ou la seule façon est de les comparer en couple et construire le résultat à partir de résultats partiels?
6 Réponses :
Vous pouvez le faire en suivant les étapes suivantes:
map () imbriquée pour ajouter une autre propriété avec un nom key dont la valeur sera la valeur sur laquelle l'objet doit être trié. li >
flat () pour créer un tableau 1D sort () puis sort () sur la base de la valeur de cette clé que nous avons ajoutée dans la carte.
const documents = [ { documentId: 'ADB0125A', fileName: 'test_2018.pdf', date: '2017-12-02T19:08:52+01:00' }, { documentId: '123456', fileName: 'test2_2018.pdf', date: '2017-12-12T22:08:52+01:00' }, { documentId: '121212', fileName: 'test3_2018.pdf', date: '2018-05-22T23:08:52+01:00' }]; const conversations = [ { conversationId: '1102', lastUpdate: '2015-10-10T18:19:12+01:00' }, { conversationId: '5622', lastUpdate: '2019-08-16T18:19:12+01:00' }, { conversationId: '112', lastUpdate: '2015-10-26T18:19:12+01:00' }]; const invoices = [ { invoiceId: "20100392077", rechnungsDatum: "2019-02-10" }, { invoiceId: "5550392077", rechnungsDatum: "2018-02-05" }, { invoiceId: "3336392077", rechnungsDatum: "2018-12-11" }];
let keys = ['date','lastUpdate','rechnungsDatum'];
const res = [documents,conversations,invoices]
.map((x,i) => x.map(a => (
{...a,key:a[keys[i]]}))
).flat()
.sort((a,b) => a.key.localeCompare(b.key))
.slice(-4)
.reverse()
.map(({key,...rest}) => rest)
console.log(res)
C'est le problème - l'OP peut ne pas connaître le nom de la propriété de date.
Je viens de faire un commentaire à ce sujet. Les noms des propriétés que je souhaite trier me sont connus (voir les commentaires "Champ à trier par" dans la question)
@Francesco J'ai mis à jour la réponse. Vérifiez si cela fonctionne.
@Francesco j'attends votre réponse
@MaheerAli J'étais en réunion désolé. Je n'ai pas encore testé votre code. Cependant, après avoir discuté de la suggestion de noox (dans les commentaires ci-dessus), nous avons décidé d'essayer d'utiliser une interface pour les 3 DTO afin d'avoir un champ de tri unifié. Je vais cependant encore tester votre solution et Nina!
Vous pouvez écrire quelque chose comme ci-dessous,
const result = [
{
conversationId: '5622',
lastUpdate: '2019-08-16T18:19:12+01:00'
},
{
invoiceId: "20100392077",
rechnungsDatum: "2019-02-10"
},
{
invoiceId: "3336392077",
rechnungsDatum: "2018-12-11"
},
{
documentId: '121212',
fileName: 'test3_2018.pdf',
date: '2018-05-22T23:08:52+01:00'
}
]
let fields = ["date","rechnungsDatum","lastUpdate"];
result.sort( (a,b)=> {
let dateA, dateB;
fields.forEach( field =>{
if(a.hasOwnProperty(field))
dateA = new Date(a[field]);
if(b.hasOwnProperty(field))
dateB = new Date(b[field]);
});
return dateA - dateB;});
console.log(result.reverse());
Faites-moi savoir si cela a fonctionné.
@Francesco s'il vous plaît vérifier la réponse, s'il vous plaît laissez-moi savoir si cela a fonctionné.
Vous pouvez prendre un objet avec les clés de remplacement pour la propriété de date souhaitée et un tableau plat qui conserve la source et une propriété de date normalisée, trier le tableau et récupérer les quatre éléments supérieurs.
À la fin, supprimez les éléments inutiles. informations et récupérez la structure de l'objet d'origine.
.as-console-wrapper { max-height: 100% !important; top: 0; }
const
documents = [{ documentId: 'ADB0125A', fileName: 'test_2018.pdf', date: '2017-12-02T19:08:52+01:00' }, { documentId: '123456', fileName: 'test2_2018.pdf', date: '2017-12-12T22:08:52+01:00' }, { documentId: '121212', fileName: 'test3_2018.pdf', date: '2018-05-22T23:08:52+01:00' }],
conversations = [{ conversationId: '1102', lastUpdate: '2015-10-10T18:19:12+01:00' }, { conversationId: '5622', lastUpdate: '2019-08-16T18:19:12+01:00' }, { conversationId: '112', lastUpdate: '2015-10-26T18:19:12+01:00' }],
invoices = [{ invoiceId: "20100392077", rechnungsDatum: "2019-02-10" }, { invoiceId: "5550392077", rechnungsDatum: "2018-02-05" }, { invoiceId: "3336392077", rechnungsDatum: "2018-12-11" }]
keys = { documents: 'date', conversations: 'lastUpdate', invoices: 'rechnungsDatum' },
result = Object
.entries({ documents, conversations, invoices })
.reduce((r, [k, v]) => [...r, ...v.map(payload => ({ payload, date: payload[keys[k]] }))], [])
.sort((a, b) => b.date.localeCompare(a.date))
.slice(0, 4)
.map(({ payload }) => payload);
console.log(result);
Je devrais changer le .entries () car il n'est pas pris en charge par IE (probablement faisable avec polyfills)
@Francesco J'aimerais avoir lu cela une heure auparavant, je suppose que WeakMap n'est pas non plus pris en charge.
Vous pouvez ajouter un champ sortBy à chaque objet de tous les tableaux. Puis fusionnez le tableau d'objets et triez en fonction du champ personnalisé. Obtenez les 4 meilleurs résultats. Supprimez ensuite la colonne sortBy :
const documents=[{documentId:'ADB0125A',fileName:'test_2018.pdf',date:'2017-12-02T19:08:52+01:00'},{documentId:'123456',fileName:'test2_2018.pdf',date:'2017-12-12T22:08:52+01:00'},{documentId:'121212',fileName:'test3_2018.pdf',date:'2018-05-22T23:08:52+01:00'}],
conversations=[{conversationId:'1102',lastUpdate:'2015-10-10T18:19:12+01:00'},{conversationId:'5622',lastUpdate:'2019-08-16T18:19:12+01:00'},{conversationId:'112',lastUpdate:'2015-10-26T18:19:12+01:00'}],
invoices=[{invoiceId:"20100392077",rechnungsDatum:"2019-02-10"},{invoiceId:"5550392077",rechnungsDatum:"2018-02-05"},{invoiceId:"3336392077",rechnungsDatum:"2018-12-11"}];
const addSortBy = (arr, sortBy) => arr.map(a => ({ ...a, sortBy: new Date(a[sortBy]) })),
takeCount = 4; // number of items needed in the output
// has all objects with an extra key which field to sortby
const merged = [...addSortBy(documents, "date"),
...addSortBy(conversations, "lastUpdate"),
...addSortBy(invoices, "rechnungsDatum")]
// sort based on the date property in sortBy
const output = merged.sort((a, b) => b.sortBy - a.sortBy)
.slice(0, takeCount)
.map(({ sortBy, ...rest }) => rest); // remove the sortBy field from objects
console.log(output)
Cette approche ne couvrirait pas mon cas. J'ai besoin des 4 éléments "les plus récents" des trois tableaux. Il se peut donc que les 4 éléments appartiennent au même tableau ou à toute autre permutation possible.
@Francesco Alors, pourquoi les factures renvoient-elles 2 derniers articles? Est-ce toujours réglé?
les factures renvoient 2 résultats car il en a 2 parmi les (4) derniers résultats. Il s'agit simplement de renvoyer le dernier champ "type de date".
@adiga Je pense que vous avez mal compris. Il veut convertir les trois tableaux en tableau 1D. Et puis sort () objet de différents tableaux à trier en fonction de leurs différentes clés. Et puis il veut les quatre derniers éléments parmi ceux-ci.
@MaheerAli il n'y avait pas de mention de 4 dans la question initiale, donc je me suis trompé. Mis à jour maintenant
Vous pouvez utiliser une WeakMap pour stocker une référence à la propriété d'objet par laquelle le tableau doit être trié et rechercher simplement la valeur en utilisant la propriété stockée.
<script>
const
documents = [{ documentId: 'ADB0125A', fileName: 'test_2018.pdf', date: '2017-12-02T19:08:52+01:00' }, { documentId: '123456', fileName: 'test2_2018.pdf', date: '2017-12-12T22:08:52+01:00' }, { documentId: '121212', fileName: 'test3_2018.pdf', date: '2018-05-22T23:08:52+01:00' }],
conversations = [{ conversationId: '1102', lastUpdate: '2015-10-10T18:19:12+01:00' }, { conversationId: '5622', lastUpdate: '2019-08-16T18:19:12+01:00' }, { conversationId: '112', lastUpdate: '2015-10-26T18:19:12+01:00' }],
invoices = [{ invoiceId: "20100392077", rechnungsDatum: "2019-02-10" }, { invoiceId: "5550392077", rechnungsDatum: "2018-02-05" }, { invoiceId: "3336392077", rechnungsDatum: "2018-12-11" }]
</script>
const flatten = (flat, arr) => [...flat, ...arr];
const PropMap = (props, arrs) => {
let map = new WeakMap();
for (let i=-1, arr; arr = arrs [++i];)
for (let j=0, obj; obj = arr [j++];)
map.set (obj, props [i])
return map;
}
PropMap.SortBy = map => (a,b) => {
let [l, r] = [a,b].map (obj => obj [map.get (obj)])
return l>r?-1:l<r?1:0
}
const data = [documents, conversations, invoices];
const props = ["date", "lastUpdate", "rechnungsDatum"]
const map = PropMap (props,data);
console.log (
data.reduce (flatten, []).sort (PropMap.SortBy (map)).slice (0,4)
)
Voici une autre approche qui, je pense, améliore la lisibilité:
topFourNewest (
{date: documents},
{lastUpdate: conversations},
{rechnungsDatum: invoices}
)
Notre petite fonction d'aide by enveloppe chaque valeur d'un tableau dans un tableau à deux éléments, avec le champ clé en premier et l'objet entier en second. (Une alternative tout aussi bonne serait de les envelopper dans des objets {key, value} .)
Il y a quatre étapes dans la fonction principale: réduire combine ces listes générées dans par en une seule. sort , évidemment, les trie. (Il les trie par ordre décroissant, pour ce problème, mais si je devais rendre cela plus générique, je ferais probablement un tri naturel et les inverserais dans une fonction séparée. Cela impliquerait seulement de permuter -1 et 1 .) map extrait simplement les objets d'origine. Et slice choisit les quatre premiers éléments.
Il existe plusieurs variantes que nous pourrions imaginer qui ignorent la fonction d'assistance, et aucune ne serait difficile à écrire. Ils peuvent avoir des API comme celles-ci:
topFourNewest (
( prop ('date'), documents ),
( prop ('lastUpdate'), conversations ),
( prop ('rechnungsDatum'), invoices )
)
ou
topFourNewest (
{key: 'date', data: documents},
{key: 'lastUpdate', data: conversations},
{key: 'rechnungsDatum', data: invoices}
)
ou, avec une fonction d'assistance ( const prop = (name) => (obj) => obj [name] ), nous pourrions avoir une flexibilité supplémentaire:
topFourNewest ( 'date', documents, 'lastUpdate', conversations, 'rechnungsDatum', invoices )
ou même
const by = (name) => (data) => data.map(d => [d[name], d])
const topFourNewest = (...types) => types
.reduce ( (a, b) => a.concat(b) )
.sort ( (a, b, aa = a[0], bb = b[0]) => aa < bb ? 1 : aa > bb ? -1 : 0 )
.map ( ([name, value]) => value )
.slice (0, 4)
const documents = [{documentId:"ADB0125A",fileName:"test_2018.pdf",date:"2017-12-02T19:08:52+01:00"},{documentId:"123456",fileName:"test2_2018.pdf",date:"2017-12-12T22:08:52+01:00"},{documentId:"121212",fileName:"test3_2018.pdf",date:"2018-05-22T23:08:52+01:00"}]
const conversations = [{conversationId:"1102",lastUpdate:"2015-10-10T18:19:12+01:00"},{conversationId:"5622",lastUpdate:"2019-08-16T18:19:12+01:00"},{conversationId:"112",lastUpdate:"2015-10-26T18:19:12+01:00"}]
const invoices=[{invoiceId:"20100392077",rechnungsDatum:"2019-02-10"},{invoiceId:"5550392077",rechnungsDatum:"2018-02-05"},{invoiceId:"3336392077",rechnungsDatum:"2018-12-11"}]
console .log (
topFourNewest (
by ('date') (documents),
by ('lastUpdate') (conversations),
by ('rechnungsDatum') (invoices)
)
)Mais j'aime la façon dont cette version initiale se lit.
Il y a plusieurs variantes qui pourraient être utiles. Nous pourrions paramétrer le nombre 4 ; il est logique que nous voulions les trois premiers ou les dix premiers. Et nous pourrions séparer la combinaison et le tri, en utilisant une deuxième fonction pour extraire le nombre de valeurs que nous voulons choisir. Ce serait très facile à faire.
veuillez ajouter le résultat souhaité. à quoi cela devrait-il ressembler?
Eh bien, comment pourriez-vous détecter cela? Vous penseriez simplement passer en revue et vérifier si la date n'est pas valide - mais regardez
factureId:new Date (20100392077) == Sam 22 août 1970 01:26:32 GMT + 1000 ( AEST)(une date réelle, pas une erreur / une date invalide).@NinaScholz: vient d'ajouter le résultat souhaité.
@JackBashford Je sais quelles sont les propriétés cibles pour chaque tableau (je les ai commentées dans le code dans ma question). Par conséquent, factureId ne serait pas au centre du tri (même s'il est pertinent de récupérer éventuellement l'objet relatif).
Si les objets sont sous votre contrôle, vous pourriez probablement créer des classes avec une classe ancêtre commune et ajouter une propriété comme
sortByDate. Si vous devez par la suite afficher ces 4 derniers éléments dans une liste, cela peut également aider si vous avez des propriétés (commedisplayName).le moment est-il pertinent?
@NinaScholz: oui, nous devrions également considérer l'heure.
@noox belle allusion. nous obtenons le DTO du backend, mais j'aurais la possibilité "d'enrichir" le modèle avec une propriété supplémentaire utilisée uniquement pour le tri. Cela faciliterait le tri global.