2

I have an array of objects in javascript that i want to reduce. See code below. message with 6 or more digits is verified, fewer is unverified. I group them by group.

const myArray = [
 { group: 'groupA', message: 'Text without a number', sl: '1A' },
 { group: 'groupA', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupB', message: 'Text without a number', sl: '1A' },
 { group: 'groupA', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupB', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupC', message: 'Text without a number', sl: '1B' },
 { group: 'groupD', message: 'Text with a number Tick0127866', sl: '1B' },
 { group: 'groupC', message: 'Text without a number', sl: '1A' }
];
output = myArray.reduce((acc, line) => {
 let yes = 0;
 let no = 0;
 line.message.match(/\d{6,}/) ? yes++ : no++;
 acc[line.group] = acc[line.group] || {};
 acc[line.group].verified = (acc[line.group].verified || 0) + yes;
 acc[line.group].unverified = (acc[line.group].unverified || 0) + no;
 return acc;
}, {});
console.log('output', output);

However I would like the output to also be an array of objects like so:

[
 { group: 'groupA', verified: 2, unverified: 1 },
 { group: 'groupB', verified: 1, unverified: 1 },
 { group: 'groupC', verified: 0, unverified: 2 },
 { group: 'groupD', verified: 1, unverified: 0 }
]

How would I do this?

asked Jan 27, 2020 at 16:46
3
  • 2
    Change the logic and use an array (myArray.reduce((acc, line) => ..., [])), or convert the result afterwards with Object.entries() + .map() Commented Jan 27, 2020 at 16:50
  • Can you provide some context regarding why you are trying to achieve? What do you use/need the verified and unverified properties for? Commented Jan 27, 2020 at 16:59
  • Thanks for the swift answers guys! I'll be using the output as input to a d3.js stacked bar chart with monthly figures. Commented Jan 27, 2020 at 17:16

6 Answers 6

3

Use an array as the initial value of your accumulator and inside .reduce , use findIndex to check for the current group, if found, update verified and unverified values, otherwise, insert a new one :

const myArray = [
 { group: "groupA", message: "Text without a number", sl: "1A" },
 { group: "groupA", message: "Text with a number WO5467829", sl: "1A" },
 { group: "groupB", message: "Text without a number", sl: "1A" },
 { group: "groupA", message: "Text with a number WO5467829", sl: "1A" },
 { group: "groupB", message: "Text with a number WO5467829", sl: "1A" },
 { group: "groupC", message: "Text without a number", sl: "1B" },
 { group: "groupD", message: "Text with a number Tick0127866", sl: "1B" },
 { group: "groupC", message: "Text without a number", sl: "1A" }
];
output = myArray.reduce((acc, line) => {
 let yes = 0;
 let no = 0;
 line.message.match(/\d{6,}/) ? yes++ : no++;
 // check if you have the group in the accumulator
 const ndx = acc.findIndex(e => e.group === line.group);
 // if you have it, manipulate verified and unverified
 if (ndx > -1) {
 acc[ndx].verified = (acc[ndx].verified || 0) + yes;
 acc[ndx].unverified = (acc[ndx].unverified || 0) + no;
 } else {
 // insert a new entry instead
 acc.push({
 group: line.group,
 verified: yes,
 unverified: no
 });
 }
 return acc;
}, []); // use an array as the initial value of the accumulator
console.log(output);

answered Jan 27, 2020 at 16:54
Sign up to request clarification or add additional context in comments.

Comments

2

You can simply extend your solution/output to get the actual required output using Object.entries() or Object.keys(). I am going to use Object.entries()

const myArray = [
 { group: 'groupA', message: 'Text without a number', sl: '1A' },
 { group: 'groupA', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupB', message: 'Text without a number', sl: '1A' },
 { group: 'groupA', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupB', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupC', message: 'Text without a number', sl: '1B' },
 { group: 'groupD', message: 'Text with a number Tick0127866', sl: '1B' },
 { group: 'groupC', message: 'Text without a number', sl: '1A' }
];
output = myArray.reduce((acc, line) => {
 let yes = 0;
 let no = 0;
 line.message.match(/\d{6,}/) ? yes++ : no++;
 acc[line.group] = acc[line.group] || {};
 acc[line.group].verified = (acc[line.group].verified || 0) + yes;
 acc[line.group].unverified = (acc[line.group].unverified || 0) + no;
 return acc;
}, {});
console.log('output', output);
const actualOutput = Object.entries(output).map(([group, value]) => ({group, ...value }));
console.log(actualOutput);

answered Jan 27, 2020 at 16:52

Comments

2

I couldn't resist refactoring your code a little. That said. what I have done:

  • initiate the accumulator with an array ( [] )
  • push a group to the array if it is not yet initialised
  • increment verified or unverified depending whether your test fails

const myArray = [
 { group: "groupA", message: "Text without a number", sl: "1A" },
 { group: "groupA", message: "Text with a number WO5467829", sl: "1A" },
 { group: "groupB", message: "Text without a number", sl: "1A" },
 { group: "groupA", message: "Text with a number WO5467829", sl: "1A" },
 { group: "groupB", message: "Text with a number WO5467829", sl: "1A" },
 { group: "groupC", message: "Text without a number", sl: "1B" },
 { group: "groupD", message: "Text with a number Tick0127866", sl: "1B" },
 { group: "groupC", message: "Text without a number", sl: "1A" }
];
output = myArray.reduce((acc, line) => {
 if( acc.findIndex(e => e.group === line.group) === -1 ) {
 	acc.push({
 group: line.group,
 verified: 0,
 unverified: 0
 });
 }
 
 let group = acc.find(e => e.group === line.group);
 if( line.message.match(/\d{6,}/) )
 	group.verified++;
 else
 	group.unverified++;
 return acc;
}, []);
console.log(output);

answered Jan 27, 2020 at 21:37

Comments

1

Put the groupprop in your acc. Then use Object.values function to return the values of an object.

const myArray = [
 { group: 'groupA', message: 'Text without a number', sl: '1A' },
 { group: 'groupA', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupB', message: 'Text without a number', sl: '1A' },
 { group: 'groupA', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupB', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupC', message: 'Text without a number', sl: '1B' },
 { group: 'groupD', message: 'Text with a number Tick0127866', sl: '1B' },
 { group: 'groupC', message: 'Text without a number', sl: '1A' }
];
output = myArray.reduce((acc, line) => {
 let yes = 0;
 let no = 0;
 line.message.match(/\d{6,}/) ? yes++ : no++;
 acc[line.group] = acc[line.group] || {};
 acc[line.group].group = line.group;
 acc[line.group].verified = (acc[line.group].verified || 0) + yes;
 acc[line.group].unverified = (acc[line.group].unverified || 0) + no;
 return acc;
}, {});
console.log('output', Object.values(output));

answered Jan 27, 2020 at 16:50

5 Comments

Someone seems to be downvoting all the answers.
Might just be punishment for answering questions that don't show attempt. But I think the OP's code is close enough, it's not a "write my code for me" question.
@Barmar This attempt is enough. I mean its too much in comparison with the question I usually see of this type.
@MaheerAli Exactly. The reduce code is the hard part.
thanks, was not me downvoting, happy with the assistance
1

You can use map() on Object.entries. I also refactored your code in reduce method

const myArray = [
 { group: 'groupA', message: 'Text without a number', sl: '1A' },
 { group: 'groupA', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupB', message: 'Text without a number', sl: '1A' },
 { group: 'groupA', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupB', message: 'Text with a number WO5467829', sl: '1A' },
 { group: 'groupC', message: 'Text without a number', sl: '1B' },
 { group: 'groupD', message: 'Text with a number Tick0127866', sl: '1B' },
 { group: 'groupC', message: 'Text without a number', sl: '1A' }
];
let output = myArray.reduce((acc, line) => {
 acc[line.group] = acc[line.group] || {verified: 0, unverified: 0};
 if(line.message.match(/\d{6,}/)){
 acc[line.group].verified++
 }
 else{
 acc[line.group].unverified++
 }
 return acc;
}, {});
output = Object.entries(output).map(([k, v]) => ({group: k, ...v}))
console.log('output', output);

answered Jan 27, 2020 at 16:50

1 Comment

Someone seems to be downvoting all the answers here.
1

Use reduce in combination with regex testing like this -

const myArray = [{
 group: 'groupA',
 message: 'Text without a number',
 sl: '1A'
 },
 {
 group: 'groupA',
 message: 'Text with a number WO5467829',
 sl: '1A'
 },
 {
 group: 'groupB',
 message: 'Text without a number',
 sl: '1A'
 },
 {
 group: 'groupA',
 message: 'Text with a number WO5467829',
 sl: '1A'
 },
 {
 group: 'groupB',
 message: 'Text with a number WO5467829',
 sl: '1A'
 },
 {
 group: 'groupC',
 message: 'Text without a number',
 sl: '1B'
 },
 {
 group: 'groupD',
 message: 'Text with a number Tick0127866',
 sl: '1B'
 },
 {
 group: 'groupC',
 message: 'Text without a number',
 sl: '1A'
 }
];
const numberRegex = /[0-9]{6}/;
const res = Object.values(myArray.reduce((acc, curr) => {
 const { group, message } = curr;
 const isPhoneVerified = numberRegex.test(message);
 if (!acc[group] && isPhoneVerified) {
 acc[group] = { group: group, verified: 1, unverified: 0 };
 } else if (!acc[group] && !isPhoneVerified) {
 acc[group] = { group: group, verified: 0, unverified: 1 };
 } else if (acc[group] && isPhoneVerified) {
 acc[group].verified += 1;
 } else if (acc[group] && !isPhoneVerified) {
 acc[group].unverified += 1;
 }
 return acc;
}, {}));
console.log(res);

answered Jan 27, 2020 at 17:06

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.