简体   繁体   中英

Converting nested array to object, matching array index with object id

i'm currently struggling with a redux reducer.

 //backend response const response = { data: { results: { 222: { items: ['id1', 'id3'] }, 333: { items: ['id2', 'id4', 'id999 (UNKNOWN)'] } } } }; //currently saved in redux state const stateItems = [ { id: 'id1', name: 'item ONE' }, { id: 'id2', name: 'item TWO' }, { id: 'id3', name: 'item THREE' }, { id: 'id4', name: 'item FOUR' }, { id: 'id5', name: 'item FIVE (UNUSED)' }, { id: 'id6', name: 'item SIX (UNUSED)' } ]; //converting items: ['ids'] => items: [{id: 'id', name: 'itemName'}] const result = Object.values(response.data.results).map((keys, index, array) => { keys.items = keys.items.map(itemId => { return stateItems[stateItems.findIndex(x => x.id === itemId)]; }); return response.data.results; }); //final result should be: const expectedFinalResult = { 222: {items: [{id: 'id1', name: 'item ONE'}, {id: 'id3', name: 'item THREE'}]}, 333: {items: [{id: 'id2', name: 'item TWO'}, {id: 'id4', name: 'item FOUR'}]} }; //both should be equal: console.log(JSON.stringify(expectedFinalResult)); console.log(JSON.stringify(result)); console.log('same result: ' + JSON.stringify(result) === JSON.stringify(expectedFinalResult)); 

I ran out of ideas, how to realize it. UNUSED and UNKNOWN should be filtered out as well. So that the final result in this example just be like in the const expectedFinalResult . Currently the const result return a wrong result back.

Hopefully someone have an better idea oder better approach.

Thank you

You're on the right track with Object.entries . You can use destructuring to pick out the key ( '222' , '333' ) and the value object's items array, then use that array to filter stateItems and produce the items array for each entry in the result:

const result = {};
for (const [key, {items}] of Object.entries(response.data.results)) {
    result[key] = {
        items: stateItems.filter(item => items.includes(item.id))
    };
}

Live Example:

 //backend response const response = { data: { results: { 222: { items: ['id1', 'id3'] }, 333: { items: ['id2', 'id4', 'id999 (UNKNOWN)'] } } } }; //currently saved in redux state const stateItems = [ { id: 'id1', name: 'item ONE' }, { id: 'id2', name: 'item TWO' }, { id: 'id3', name: 'item THREE' }, { id: 'id4', name: 'item FOUR' }, { id: 'id5', name: 'item FIVE (UNUSED)' }, { id: 'id6', name: 'item SIX (UNUSED)' } ]; const result = {}; for (const [key, {items}] of Object.entries(response.data.results)) { result[key] = { items: stateItems.filter(item => items.includes(item.id)) }; } //final result should be: const expectedFinalResult = { 222: {items: [{id: 'id1', name: 'item ONE'}, {id: 'id3', name: 'item THREE'}]}, 333: {items: [{id: 'id2', name: 'item TWO'}, {id: 'id4', name: 'item FOUR'}]} }; //both should be equal: console.log(JSON.stringify(result, null, 4)); 
 .as-console-wrapper { max-height: 100% !important; } 

That makes multiple passes through stateItems . If it or response.data.results is really, really, really, really big (like hundreds of thousands), it may be worthwhile to do a Map of the stateItem s by id instead:

// Create map of state items (only once each time stateItems changes):
const stateItemMap = new Map(stateItems.map(item => [item.id, item]));

// Map results (each time you get results):
const result = {};
for (const [key, {items}] of Object.entries(response.data.results)) {
    result[key] = {
        items: items.map(id => stateItemMap.get(id))
    };
}

Live Example:

 //backend response const response = { data: { results: { 222: { items: ['id1', 'id3'] }, 333: { items: ['id2', 'id4', 'id999 (UNKNOWN)'] } } } }; //currently saved in redux state const stateItems = [ { id: 'id1', name: 'item ONE' }, { id: 'id2', name: 'item TWO' }, { id: 'id3', name: 'item THREE' }, { id: 'id4', name: 'item FOUR' }, { id: 'id5', name: 'item FIVE (UNUSED)' }, { id: 'id6', name: 'item SIX (UNUSED)' } ]; // Create map of state items (only once each time stateItems changes): const stateItemMap = new Map(stateItems.map(item => [item.id, item])); // Map results (each time you get results): const result = {}; for (const [key, {items}] of Object.entries(response.data.results)) { result[key] = { items: items.map(id => stateItemMap.get(id)) }; } //final result should be: const expectedFinalResult = { 222: {items: [{id: 'id1', name: 'item ONE'}, {id: 'id3', name: 'item THREE'}]}, 333: {items: [{id: 'id2', name: 'item TWO'}, {id: 'id4', name: 'item FOUR'}]} }; //both should be equal: console.log(JSON.stringify(result, null, 4)); 
 .as-console-wrapper { max-height: 100% !important; } 

You could do it with reduce:

Object.entries(response.data.results)
    .reduce((acc, [key, { items }]) => ({
        ...acc,
        [key]: { // we can use the dynamic keys to add in our accumulated object
            items: items
                .map(itemId => stateItems.find(x => x.id === itemId)) // you can use find directly instead of findIndex then access
                .filter(Boolean) // we skip the unneeded elements
        }
    }), {});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM