简体   繁体   中英

forEach method seems to skip an entry

Aim

I am trying to make a function which would take an array of "votes" and return an object containing the number of times each "candidate" received a score of 1.

For example an array such as:

[
    {
        "blue": 1,
        "red": 3,
        "purple": 2,
        "yellow": 4
    },
    {
        "blue": 2,
        "red": 3,
        "purple": 4,
        "yellow": 1
    },
    {
        "blue": 1,
        "red": 2,
        "purple": 4,
        "yellow": 3
    },
    {
        "blue": 3,
        "red": 4,
        "purple": 2,
        "yellow": 1
    }
];

should return an object

{
    "blue": 2,
    "red": 0,
    "purple": 0,
    "yellow": 2
}

Current Code

Currently I have written the function

// adds up the first choice results from raw data
// return object containing number of first choice votes each candidate received
const add_first_choices = function (raw_data) {
    // create object to store number of first choices
    const storage_obj = empty_candidate_obj(raw_data);

    // loop through results, adding first choices to storage_obj
    raw_data.forEach(function (single_voter_obj) {
        Object.values(single_voter_obj).forEach(function (value) {
            if (value === 1) {
                storage_obj[getKeyByValue(single_voter_obj, value)] += 1;
            }
        });
    });
    return storage_obj;
};

which uses the following two other functions

// returns object of candidate names, each with value of 0
const empty_candidate_obj = function (raw_data) {
    const input_data = raw_data;
    let first_vote = input_data[0];
    const keys = Object.keys(first_vote);
    keys.forEach(function (key) {
        first_vote[key] = 0;
    });
    return first_vote;
};

and

// returns key from object value
const getKeyByValue = function (object, value) {
    return Object.keys(object).find((key) => object[key] === value);
};

Problem

When inputting the above array into my function it returns

{
    blue: 1,
    red: 0,
    purple: 0,
    yellow: 2
}

=> Not as expected!

Do you know what I have done wrong?

Thanks for any replies:)

The issue was with this line:

const input_data = raw_data;

Copying an array of objects is problematic because copying the array doesn't automatically make new copies of the objects. It's just a new array with references to the original objects, so if you change an object in the new array you're changing the original object.

So you need to make new copies of the objects too:

const input_data = raw_data.map(obj => ({...obj}));

You can solve your problem slightly simpler with reduce .

 const data = [{"blue":1,"red":3,"purple":2,"yellow":4},{"blue":2,"red":3,"purple":4,"yellow":1},{"blue":1,"red":2,"purple":4,"yellow":3},{"blue":3,"red":4,"purple":2,"yellow":1}]; // We initialise the `reduce` with a new `{}` object, // which is updated with each iteration. // `acc` is the accumulator, `c` is the current element const result = data.reduce((acc, c) => { // For each object in the array we grab the key and value Object.entries(c).forEach(([ key, value ]) => { // If our initial object doesn't already have a key // that matches, create one and set it to zero acc[key] = acc[key] || 0; // And if the value is one, increase the value of that property if (value === 1) acc[key]++; }); // Make sure to return the initial object // for the next iteration return acc; }, {}); console.log(result);

With respect to your code the problem is in this line of empty_candidate_obj funcation

  let first_vote = input_data[0];

Update you code to send a copy

let first_vote = {...input_data[0]} ;

Object works on reference. so it's like your first object of your dataset values are updated to 0 as well with your empty object

You probably want to use a reducer something like this:

 const results = [ { "blue": 1, "red": 3, "purple": 2, "yellow": 4 }, { "blue": 2, "red": 3, "purple": 4, "yellow": 1 }, { "blue": 1, "red": 2, "purple": 4, "yellow": 3 }, { "blue": 3, "red": 4, "purple": 2, "yellow": 1 } ]; const empty_obj = { "blue": 0, "red": 0, "purple": 0, "yellow": 0 } result = results.reduce( reducer, empty_obj) console.log( result ) function reducer( acc, val ) { for( key in val ) if( val[key] === 1 ) acc[key]++; return acc; }

Try this function:

 function candidateScore1Times(array) { const newObj = {} for (const score of array) { Object.keys(score).forEach(elem => { if (newObj[elem]?= null) { if (score[elem] == 1) { newObj[elem]++ } } else { score[elem] == 1: newObj[elem] = 1 : newObj[elem] = 0 } }) } return newObj }

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