Zliczanie kopi obiektów w tablicy i zapis ich liczby do klucza

0

Witam.
Mam tablicę z obiektami które mają dwa pola x i y,
fragment:

const array = [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 12 } ]

Chciałbym pozbyć się duplikatów obiektów, ale zachować ich liczbę jako nowe pole 'count'.

czyli powyższa tablica po sprawdzeniu powinna wyglądać tak

array = [ { x: 1, y: 2, count:3 }, { x: 3, y: 4, count:2 }, { x: 3, y: 12, count:1 } ]

Znalazłem odpowiedź, ale w tym przypadku sprawdza obiekty, które które mają jedno pole.

const names = [{  _id: 1 }, { _id: 1}, { _id: 2}, { _id: 1}]

const result = [...names.reduce( (mp, o) => {
    if (!mp.has(o._id)) mp.set(o._id, Object.assign({ count: 0 }, o));
    mp.get(o._id).count++;
    return mp;
}, new Map).values()];

console.log(result);

Jak zmodyfikować kod żebym otrzymał wspomniany wcześniej wynik?

1

Sposobów jest na pewno 'kilka'. Jeden z nich poniżej.

const array = [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 1, y: 2 }, { x: 3, y: 12 } ]

let itemWithDuplicateCounterMap = new Map();
for (let item of array) {
    let key = "x" + item.x.toString() + "y" + item.y.toString();
    let itemWithDuplicateCounter = itemWithDuplicateCounterMap.get(key);
    if (itemWithDuplicateCounter) {
        itemWithDuplicateCounter.counter++;
    } else {

        itemWithDuplicateCounter = item;
        itemWithDuplicateCounter.counter = 1;
        itemWithDuplicateCounterMap.set(key, itemWithDuplicateCounter);
    }
}

console.log(itemWithDuplicateCounterMap.values());
1

Wymyśliłem coś takiego:

const coords = [
    {x: 1, y: 2},
    {x: 3, y: 4},
    {x: 1, y: 2},
    {x: 3, y: 4},
    {x: 1, y: 2},
    {x: 3, y: 12}
];

const result = coords
    // usuwamy duplikaty
    // działa to tak, że findIndex zwraca zawsze pierwsze wytąpienie w tablicy (a reczej indeks wystąpienia)
    // więc gdy za pierwszym razem sprawdzimy {x: 1, y: 2} to będzie 0 === 0
    // za drugim razem będzie już 2 === 0, co jest false, więc ten element zostanie odrzucony
    // Czyli odrzucamy kazde kolejne wystąpienie elementu - poza pierwszym
    .filter((coord, index) => {
        return index === coords.findIndex(({x, y}) => coord.x === x && coord.y === y) // {x, y} to destructuring assignment 
    })
    // teraz liczymy duplikaty
    // a przy okazji tworzymy taką strukture, jaka nas interesuje - z policzonymi duplikatami
    // oczywiscie oryginalna tablica coords cały czas je zawiera bo ani funkcja filter, ani map 
    // NIE MODYFIKUJE TABLICY co jest super!
    .map(coord => {
        const duplicates = coords.filter(({x, y}) => coord.x === x && coord.y === y);

        return {
            ...coord, // te ... to tzw. spread operator
            count: duplicates.length
        }
    })

Możliwe, że da się inaczej, ale na bank to co zaproponowałem jest czytelniejsze, niż to co wyżej wyczarowałeś :)

1

@bobojak: jeżeli nie podoba Ci się inline, to można też tak:

const coords = [
    {x: 1, y: 2},
    {x: 3, y: 4},
    {x: 1, y: 2},
    {x: 3, y: 4},
    {x: 1, y: 2},
    {x: 3, y: 12}
];

const equals = (x, props) => y => {
    return props.reduce((acc, prop) => acc && x[prop] === y[prop], true);
};

const unique = (...props) => (item, index, array) => {
    return index === array.findIndex(equals(item, props))
};

const addDuplicatesCount = (...props) => (item, index, array) => {
    const duplicates = array.filter(equals(item, props));

    return {
        ...item,
        count: duplicates.length
    }
};

const result = coords
    .map(addDuplicatesCount('x', 'y'))
    .filter(unique('x', 'y'));

Dzięki czemu mamy większą elastyczność:

const coords = [
    {x: 1, y: 2, z: 3},
    {x: 3, y: 4, z: 7},
    {x: 1, y: 2, z: 3},
    {x: 3, y: 4, z: 7},
    {x: 1, y: 2, z: 0},
    {x: 3, y: 12, z: 3}
];


const result = coords
    .map(addDuplicatesCount('x', 'y', 'z'))
    .filter(unique('x', 'y', 'z'));

1 użytkowników online, w tym zalogowanych: 0, gości: 1