0

I have 3 people in the house ['John', 'Jane', 'Jack'],

We kept track of who opened/closed the door.

logs = [
  { name: "John", status: "opened" },
  { name: "Jane", status: "opened" },
  { name: "Jack", status: "opened" },
  { name: "Jane", status: "closed" },
  { name: "Jack", status: "closed" },
];

As you can see only 2 people ['Jane', 'Jack'] that opened and closed the door properly.

programmatically, I did these 3 steps to come to know that John is the person who opened and never closed the door.

let openers = logs.reduce((acc, log) => {
  if (log.status === "opened") {
    acc.push(log.name);
  }
  return acc;
}, []);

console.log(openers);

let closers = logs.reduce((acc, log) => {
  if (log.status === "closed") {
    acc.push(log.name);
  }
  return acc;
}, []);

console.log(closers);

let result = [];
closers.forEach((closer) => {
  if (openers.includes(closer)) {
    result.push(closer);
  }
});

console.log(result);

I was trying to do these in one go in a single reduce(), but I'm not too sure.

Can someone help me improve what I got

logs = [
  { name: "John", status: "opened" },
  { name: "Jane", status: "opened" },
  { name: "Jack", status: "opened" },
  { name: "Jane", status: "closed" },
  { name: "Jack", status: "closed" },
];

let openers = logs.reduce((acc, log) => {
  if (log.status === "opened") {
    acc.push(log.name);
  }
  return acc;
}, []);

console.log(openers);

let closers = logs.reduce((acc, log) => {
  if (log.status === "closed") {
    acc.push(log.name);
  }
  return acc;
}, []);

console.log(closers);

let result = [];
closers.forEach((closer) => {
  if (openers.includes(closer)) {
    result.push(closer);
  }
});

console.log(result);

?

1
  • 1
    From the question it sounds like there is only one door, and it seems that the log entries are supposed to be in sequence. However that would mean that a door that is opened can be opened again, which doesn't make sense. Does every person have their own door? Commented Dec 9, 2022 at 6:39

4 Answers 4

2

You can do that with a single .reduce() that keeps track of door status by name:

logs = [
  { name: "John", status: "opened" },
  { name: "Jane", status: "opened" },
  { name: "Jack", status: "opened" },
  { name: "Jane", status: "closed" },
  { name: "Jack", status: "closed" },
];

let statuses = logs.reduce((acc, log) => {
  acc[log.name] = (log.status === 'opened');
  return acc;
}, {});
console.log(statuses);
let culprits = Object.keys(statuses).filter(name => statuses[name]);
console.log(culprits);

Output:

{
  "John": true,
  "Jane": false,
  "Jack": false
}
[
  "John"
]
Sign up to request clarification or add additional context in comments.

2 Comments

I like your solution a lot. How comes your codes never look for when someone closed ? Did I overlook something ? Can you please give me inputs on that a bit more ?
The door only has two states, that's why it is sufficient to check only for (log.status === 'opened')
1

It's bound to be a little more involved, because people can open and close the door multiple times.

This approach finds the people for which the number of openings/closings don't match. It reduces the input to an intermediate object where for every name, it adds 1 to the value if the door was opened, and subtracts one if the door was closed. After that, it's just a matter of getting the object's entries, and filtering out the names for which the count is 0.

const logs = [
  { name: "John", status: "opened" },
  { name: "Jane", status: "opened" },
  { name: "Jack", status: "opened" },
  { name: "Jane", status: "closed" },
  { name: "Jack", status: "closed" },
];

const result = Object.entries(logs.reduce((a, v) => {
  a[v.name] = a[v.name] || 0;
  a[v.name] += v.status === 'opened' ? 1 : -1;
  return a;
}, {})).filter(([_, v]) => v).map(([k]) => k);

console.log(result);

There are additional complexities if you would take the order of openings and closings into account, but the question does not provide enough details to address those.

2 Comments

I think keeping track of count open/close is overkill because you can't open a door twice, then close twice, e.g. are always open & close sequences
@PeterThoeny Then again, from the input it looks like the door is opened three times in sequence, and then closed twice. Unless every person has their own door? Not enough details provided in the question...
0

Here is another way:

const result = logs.reduce((accVal, x) => {
    if (logs.find(y => x.name == y.name && x.status == "opened" && y.status == "closed" )) {
        return Object.assign(accVal, accVal.opened_and_closed.splice(logs.length, 0, x.name));
    }
    else if (logs.find(y => x.name == y.name && x.status == "opened" && y.status != "closed" )) {
        return Object.assign(accVal, accVal.opened_not_closed.splice(logs.length, 0, x.name));
    }
    else {
        return accVal;
    }
},
{ "opened_and_closed": [ ], "opened_not_closed": [ ] } 
);

The result value:

{
  opened_and_closed: [ 'Jane', 'Jack' ],
  opened_not_closed: [ 'John' ]
}

13 Comments

that has an exponential time complexity of O(n^2).
@RobbyCornelissen Thanks for the comment. Please explain. Also, The question post has no such requirement.
@RobbyCornelissen "I was trying to do these in one go in a single reduce(), but I'm not too sure." - That was the requirement of the question post.
@RobbyCornelissen "I have 3 people in the house ['John', 'Jane', 'Jack']," (from the question post). How does the "time compexity" apply to this data?
In this case, time complexity is expressed in terms of the number of items in the log array, not the number of people in the house. For the current example input (i.e. 5 entries), the difference is negligible. For a couple of thousands of entries, your solution is going to slow down significantly. For a couple of million entries, the galaxy will implode before your algorithm finishes computation. Time complexity always needs to be factored in.
|
0

Assuming, as other answerers have, that there should always be a open/close sequence for a given person, here's a solution that uses a single reduce() statement with a Set to keep track of who opened but hasn't closed the door:

const logs = [
  { name: "John", status: "opened" },
  { name: "Jane", status: "opened" },
  { name: "Jack", status: "opened" },
  { name: "Jane", status: "closed" },
  { name: "Jack", status: "closed" },
];

const result = logs.reduce((a, { name, status }) => {
  status === 'opened' ? a.add(name) : a.delete(name);
  return a;
}, new Set());

console.log([...result]);

This solution has a runtime complexity of O(n) and does not result in duplicate entries should one person open and close the door multiple times.

Comments

Your Answer

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.