Chai-related error message: "AssertionError: expected undefined to deeply equal" - javascript

I wrote a function that when given a list of objects and an
id, returns the same list, but with the corresponding object marked active
(all other objects should not be active).
const list = [
{ id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: true },
{ id: 4, active: false }
];
function markActive(list, value) {
list.forEach((id) => {
if (id.active = (id.id === value)) {
return true;
} else {
return false;
}
});
}
markActive(list, 2);
console.log(list)
Returns:
[ { id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: true } ]
It's working like a charm, except when I run "npm run [filename]" I get an error message:
Running Tests for [filename].
------------
[ { id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: true } ]
markActive
1) Case 1 (Given Sample)
2) Case 2 (String IDs)
0 passing (16ms)
2 failing
1) markActive Case 1 (Given Sample):
AssertionError: expected undefined to deeply equal [ { id: 1,
active: false },
{ id: 2, active: true },
{ id: 3, active: false },
{ id: 4, active: false } ]
at Function.assert.deepEqual
(node_modules/chai/lib/chai/interface/assert.js:216:32)
at Context.it (tests/test_02.js:23:12)
2) markActive Case 2 (String IDs):
AssertionError: expected undefined to deeply equal [ { id: '1',
active: false },
{ id: '2', active: true },
{ id: '3', active: false },
{ id: '4', active: false } ]
at Function.assert.deepEqual
(node_modules/chai/lib/chai/interface/assert.js:216:32)
at Context.it (tests/test_02.js:40:12)
Any idea what I'm doing wrong? Here's the code that sets up the tests:
const chai = require("chai");
const sinon = require("sinon");
const assert = chai.assert;
const markActive = require("../answers/02.js");
describe("markActive", () => {
it("Case 1 (Given Sample)", () => {
var list = [
{ id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: true },
{ id: 4, active: false }
];
var newList = markActive(list, 2);
var targetList = [
{ id: 1, active: false },
{ id: 2, active: true },
{ id: 3, active: false },
{ id: 4, active: false }
];
assert.deepEqual(newList, targetList);
});
it("Case 2 (String IDs)", () => {
var list = [
{ id: "1", active: false },
{ id: "2", active: false },
{ id: "3", active: true },
{ id: "4", active: false }
];
var newList = markActive(list, "2");
var targetList = [
{ id: "1", active: false },
{ id: "2", active: true },
{ id: "3", active: false },
{ id: "4", active: false }
];
assert.deepEqual(newList, targetList);
});
});

Your function isn't returning anything, so any variables you try to set to the result will be set as undefined.
To fix this, simply add a return statement to the end of your function.
function markActive(list, value) {
list.forEach((id) => {
if (id.active = (id.id === value)) {
return true;
} else {
return false;
}
});
return list; // return the updated list
}
NOTE: It's worth mentioning that because the array is referenced, you're modifying the values in-place. This is why the array you defined outside the function still had updated results even though you weren't logging the returned value. This can have unintended side effects if you were to run the markActive() function several times on the same list. If you want a new list to be returned, look into ways of copying and deep copying arrays in Javascript.

Related

Dealing with looping through nested arrays

I have data array, which has nested arrays inside (level1arr, leve21arr ...)
const data = [
{
level1arr: [
{
level2arr: [{ id: 1, isValid: true }, { id: 2, isValid: true }, { id: 3, isValid: true }],
},
{
level2arr: [{ id: 4, isValid: true }, { id: 5, isValid: true }, { id: 6, isValid: true }],
},
],
},
{
level1arr: [
{
level2arr: [{ id: 7, isValid: true }, { id: 8, isValid: true }, { id: 9, isValid: true }],
},
{
level2arr: [{ id: 10, isValid: true }, { id: 11, isValid: true }, { id: 12, isValid: true }],
},
],
},
];
I also have another array:
const invalidIds = [2,5]
I want to find elements with apecyfic id and change isValid property to false.
Is it better way than iteratinf over multiple nested arrays, like that:
data.forEach(lvl1 => {
lvl1.level1arr.forEach(lvl2 => {
lvl2.level2arr.forEach(element => {
// further nesting
});
});
})
Such iterating over multiple arrays is not good for performance. What is the best way to handle such case with nested arrays?
If it were nested arrays, you could use Array.prototype.flat(). However, you have a mix of nested objects and arrays. You will have to write a custom "flattener" for this data structure. Check this answer for details: how to convert this nested object into a flat object?
You can use recursion until you reach the level you need. Here's one way to do it.
const data = [{
level1arr: [{
level2arr: [{
id: 1,
isValid: true
}, {
id: 2,
isValid: true
}, {
id: 3,
isValid: true
}],
},
{
level2arr: [{
id: 4,
isValid: true
}, {
id: 5,
isValid: true
}, {
id: 6,
isValid: true
}],
},
],
},
{
level1arr: [{
level2arr: [{
id: 7,
isValid: true
}, {
id: 8,
isValid: true
}, {
id: 9,
isValid: true
}],
},
{
level2arr: [{
id: 10,
isValid: true
}, {
id: 11,
isValid: true
}, {
id: 12,
isValid: true
}],
},
],
},
];
const invalidIds =[2,5]
const findId = (object, key, value) => {
if (Array.isArray(object)) {
for (const obj of object) {
findId(obj, key, value);
}
} else {
if (object.hasOwnProperty(key) && object[key] === value) {
object.isValid = false;
return object
}
for (const k of Object.keys(object)) {
if (typeof object[k] === "object") {
findId(object[k], key, value);
}
}
}
}
invalidIds.forEach(id => findId(data, "id", id))
console.log(data)

sort an array by a boolean property in typescript

I am using angular 10 and I was wondering how can I sort this array
var dic = [
{ state: false, id: 1 },
{ state: true, id: 2} ,
{ state: false, id: 3 },
{ state: true, id: 4 },
{ state: false, id: 5 }
]
I want to sort by the value of the boolean state, so the result goes this way:
[
{ state: true, id: 2 },
{ state: true, id: 4 },
{ state: false, id: 1 },
{ state: false, id: 3 },
{ state: false, id: 5 }
]
The true value goes first in the array.
What property or something from typescript I have to use to do that?
Thank you!
You can do this using Array#sort by converting the boolean values of state:
Number(true) //1
Number(false) //0
const dic = [
{ state: false, id: 1 },
{ state: true, id: 2 },
{ state: false, id: 3 },
{ state: true, id: 4 },
{ state: false, id: 5 }
];
dic.sort(({ state: stateA = false }, { state: stateB = false }) =>
Number(stateB) - Number(stateA)
);
console.log(dic);
You can complete it by doing simplify like this.
dic.sort((a, b) => b.state - a.state);
Explain:
We all know that:
false // 0
true // 1
So
false - true // -1
true - false // 1
true - true // 0
false - false // 0
Demo
const dic = [
{ state: false, id: 1 },
{ state: true, id: 2 },
{ state: false, id: 3 },
{ state: true, id: 4 },
{ state: false, id: 5 }
];
dic.sort((a, b) => b.state - a.state);
console.log(dic);
You can sort by the state by casting the boolean to an integer value of 1 or 0 by appending a double-bang (!!) or double-tilde (~~). If the result is 0, move onto the id values.
const dic = [
{ state: false , id: 1 },
{ state: true , id: 2 },
{ state: false , id: 3 },
{ state: true , id: 4 },
{ state: false , id: 5 }
];
dic.sort(({ state: stateA, id: idA }, { state: stateB, id: idB }) =>
(!!stateB - !!stateA) || (idA - idB));
console.log(dic);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Alternatively:
(~~stateB - ~~stateA) || (idA - idB)

filter through multiple arrays and add objects from them to a single array

Example of an object in the accounts array:
const accounts = [
{
id: "5f446f2ecfaf0310387c9603",
picture: "https://api.adorable.io/avatars/75/esther.tucker#zillacon.me",
age: 25,
name: {
first: "Esther",
last: "Tucker",
},
company: "ZILLACON",
email: "esther.tucker#zillacon.me",
registered: "Thursday, May 28, 2015 2:51 PM",
},
Example of an object in the books array:
const books = [
{
id: "5f447132d487bd81da01e25e",
title: "sit eiusmod occaecat eu magna",
genre: "Science",
authorId: 8,
borrows: [
{
id: "5f446f2e2cfa3e1d234679b9",
returned: false,
},
{
id: "5f446f2ed3609b719568a415",
returned: true,
},
{
id: "5f446f2e1c71888e2233621e",
returned: true,
},
{
id: "5f446f2e6059326d9feb9a68",
returned: true,
},
{
id: "5f446f2ede05a0b1e3394d8b",
returned: true,
},
{
id: "5f446f2e4081699cdc6a2735",
returned: true,
},
{
id: "5f446f2e3900dfec59489477",
returned: true,
},
{
id: "5f446f2e6059326d9feb9a68",
returned: true,
},
{
id: "5f446f2e409f8883af2955dd",
returned: true,
},
{
id: "5f446f2e3900dfec59489477",
returned: true,
},
{
id: "5f446f2eae901a82e0259947",
returned: true,
},
{
id: "5f446f2ef2ab5f5a9f60c4f2",
returned: true,
},
{
id: "5f446f2ea6b68cf6f85f6e28",
returned: true,
},
{
id: "5f446f2eed18105706d6ca19",
returned: true,
},
{
id: "5f446f2eae901a82e0259947",
returned: true,
},
{
id: "5f446f2e91c2af00cb74e82b",
returned: true,
},
{
id: "5f446f2e5aa2bb5545a0f8a6",
returned: true,
},
{
id: "5f446f2ea508b6a99c3e42c6",
returned: true,
},
{
id: "5f446f2e50cc2da9cd80efdb",
returned: true,
},
{
id: "5f446f2e0b3e2ff72fc503e7",
returned: true,
},
{
id: "5f446f2e91c2af00cb74e82b",
returned: true,
},
{
id: "5f446f2ef795e593cd3cd19d",
returned: true,
},
{
id: "5f446f2e2f35653fa80bf490",
returned: true,
},
{
id: "5f446f2e7b9cd304fed3a8bc",
returned: true,
},
{
id: "5f446f2ed9aac23c0340aab2",
returned: true,
},
],
},
Example of objects in the authors array:
const authors = [
{
id: 0,
name: {
first: "Lucia",
last: "Moreno",
},
},
{
id: 1,
name: {
first: "Trisha",
last: "Mathis",
},
},
{
id: 2,
name: {
first: "Arnold",
last: "Marks",
},
},
I need to write the function function getBooksPossessedByAccount(account, books, authors) {} that does the following: It returns an array of books and authors that represents all books currently checked out by the given account. Look carefully at the object below, as it's not just the book object; the author object is embedded inside of it.
Output example:
getBooksPossessedByAccount(account, books, authors);
[
{
id: "5f447132320b4bc16f950076",
title: "est voluptate nisi",
genre: "Classics",
authorId: 12,
author: {
id: 12,
name: {
first: "Chrystal",
last: "Lester",
},
},
borrows: [
{
id: "5f446f2e6059326d9feb9a68",
returned: false,
},
...
],
},
]
Here's what I have so far:
function getBooksPossessedByAccount(account, books, authors) {
const accId = account.id;
const result = [];
for (let idxBks = 0; idxBks < books.length; idxBks++) {
if (
books[idxBks].borrows.id === accId &&
books[idxBks].borrows.returned === false
) {
result.push(books[idxBks]);
}
for (let idxAuth = 0; idxAuth < authors.length; idxAuth++) {
let authorIdx = authors[idxAuth];
if (authorIdx.id === result.authorId) {
return [result, { author: authorIdx }];
}
}
}
return result;
}
You need to search all the borrows, not just borrows[0]. You can use the some() method to check all of them.
Since the author information needs to be added as a property to the book object, you shouldn't be pushing it onto the booksOut array.
function getBooksPossessedByAccount(account, books, authors) {
const accId = account.id;
const booksOut = books.filter(
(book) => book.borrows.some(borrow => !borrow.returned && borrow.id === accId)
);
booksOut.forEach(book => book.author = authors.find(author => book.authorID == author.id))
return booksOut;
}
Using some should do the trick..
function getBooksPossessedByAccount(account, books, authors) {
let borrowedBooks=books.filter(book=>
book.some(borrow=>borrow.id===account.id)
)
return borrowedBooks //array of book objects
//return borrowedBooks.map(book=>book.id) //to show array of book ids
}

Push objects to new array with checked: true, if in mapped array

Can I please get some help with this scenario?
An array of strings
A function that maps the array of strings and applies to each one of them a name, key and adds one more object "checked: false".
A function that takes the mapped array and transforms it according to the argument passed, storing the value in to another array and changing the "checked" to value "true"
Ex:
const defaultProducts = [
"Laptop",
"Tablet",
"Phone",
"Ram",
"SSD",
"RasberyPi",
"Desktop",
"TV",
"Monitor"
];
const getDefaultProducts = () => {
return defaultProducts.map(products => {
return {
name: products,
checked: false
};
});
};
console.log(getDefaultProducts())
let forSale = []
function useProduct(product){
if(product in getDefaultProducts()) {
return{
product: forSale.push(product),
checked: true
};
};
return {product};
}
console.log(useProduct("Laptop"))
console.log(forSale)
returns
[ { name: 'Laptop', checked: false },
{ name: 'Tablet', checked: false },
{ name: 'Phone', checked: false },
{ name: 'Ram', checked: false },
{ name: 'SSD', checked: false },
{ name: 'RasberyPi', checked: false },
{ name: 'Desktop', checked: false },
{ name: 'TV', checked: false },
{ name: 'Monitor', checked: false } ]
{ product: 'Laptop' }
[]
Should return:
[ { name: 'Laptop', checked: false },
{ name: 'Tablet', checked: false },
{ name: 'Phone', checked: false },
{ name: 'Ram', checked: false },
{ name: 'SSD', checked: false },
{ name: 'RasberyPi', checked: false },
{ name: 'Desktop', checked: false },
{ name: 'TV', checked: false },
{ name: 'Monitor', checked: false } ]
{ product: 'Laptop' }
[{name:"Laptop", checked: true}]
In the part where you checked the condition as if product in getDefaultProducts () will not work since getDefaultProducts is an array of objects. You are comparing strings with each object such as:
"Laptop" === { name: "Laptop", checked: false }
which will return false always. Instead you can use find function:
function useProduct(product){
getDefaultProducts().find(el => {
if (el.name === product) {
el.checked = true
forSale.push(el)
}
});
return product;
}
Try with this:
function useProduct(product){
const found = getDefaultProducts().find(p => p.name === product)
if (found) {
found.checked = true
forSale.push(found)
}
return {product}
}

Transform Nested Object Data Structure into an Array of Objects- JavaScript

I am having trouble with this problem taking an object and reformatting it to a new data structure. I need to take the beginning object and do the following: sort by group first, then label and exclude "active: false" records.
var beginning = {
Sister: {
1: { id: 1, name: 'Jesse Steven', active: false },
2: { id: 2, name: 'Zena Wong', active: true },
3: { id: 3, name: 'Katie Johnson', active: true },
},
Brother: {
10: { id: 10, name: 'Jeff Jacobs', active: true },
11: { id: 11, name: 'Mark Matha', active: false },
12: { id: 12, name: 'Kyle Ford', active: true },
},
Friend: {
20: { id: 20, name: 'Jim Dobbs', active: true },
}
};
After, it should looks like this:
var final = [
{ label: 'Jeff Jacobs', value: 10, group: 'Brother' },
{ label: 'Kyle Ford', value: 12, group: 'Brother' },
{ label: 'Jim Dobbs', value: 20, group: 'Friend' },
{ label: 'Katie Johnson', value: 3, group: 'Sister' },
{ label: 'Zena Wong', value: 2, group: 'Sister' }
];
Like this?
It's still missing a sort, but that can be easily remedied.
let beginning = {
Sister: {
1: { id: 1, name: 'Jesse Steven', active: false },
2: { id: 2, name: 'Zena Wong', active: true },
3: { id: 3, name: 'Katie Johnson', active: true },
},
Brother: {
10: { id: 10, name: 'Jeff Jacobs', active: true },
11: { id: 11, name: 'Mark Matha', active: false },
12: { id: 12, name: 'Kyle Ford', active: true },
},
Friend: {
20: { id: 20, name: 'Jim Dobbs', active: true },
}
};
let relations = Object.keys(beginning)
let final = relations.map(function(relation){
let num_keys = Object.keys(beginning[relation])
return num_keys.map(function(num_key){
beginning[relation][num_key]["group"] = relation
return beginning[relation][num_key]
})
})
.reduce(function(a, b){//flattens the returned array of arrays
return a.concat(b);
})
.filter(function(a){//filters out only active
return a["active"]
})
.map(function(a){//clean up some data
return {
label: a["name"],
value: a["id"],
group: a["group"]
}
})
console.log(final)
EDIT: Added sorting as the initial requirements asked for.
You can do this a number of ways, including for / in loops or some fancy stuff with ES2015, but a relatively simple functional example solution would be the following:
var activePeople = Object.keys(beginning).map(person => {
return Object.keys(beginning[person]).map(num => {
return (!!beginning[person][num].active) ? {
label: beginning[person][num].name,
value: beginning[person][num].id,
group: person
} : null
}).filter(i => !!i)
})
// flatten nested arrays
var final = [].concat.apply([], activePeople).sort((p1, p2) => {
if (p1.group < p2.group) {
return -1
} else if (p1.group > p2.group) {
return 1
}
if (p1.label < p2.label) {
return -1
}
return 1
})
I can propose faster one code:
"use strict";
let beginning = {
Sister: {
1: { id: 1, name: 'Jesse Steven', active: false },
2: { id: 2, name: 'Zena Wong', active: true },
3: { id: 3, name: 'Katie Johnson', active: true },
},
Brother: {
10: { id: 10, name: 'Jeff Jacobs', active: true },
11: { id: 11, name: 'Mark Matha', active: false },
12: { id: 12, name: 'Kyle Ford', active: true },
},
Friend: {
20: { id: 20, name: 'Jim Dobbs', active: true },
}
};
let groups = Object.keys(beginning).sort();
let final = [];
for (let i = 0, max = groups.length; i < max; i++) {
let keys = Object.keys(beginning[groups[i]]);
for (let j = 0, max2 = keys.length; j < max2; j++) {
let item = beginning[groups[i]][keys[j]];
if (item['active'] ) {
final.push({
label: item['name'],
value: keys[j],
group: groups[i]
});
}
}
}
console.log(final);

Categories

Resources