I need to check that an Array contains an Object that matches a given structure.
I already tried this:
const myArray = [{ name: 'Mete', age: 19, phone: '123456' }];
expect(myArray).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'Mete',
age: 19
})
])
)
// Throws no error
It should not match because the object in the array has an additional property "phone".
I need something like toStrictEqual() combined with arrayContaining().
You can use Array.prototype.some() with .toEqual(value) to test each object in the array, which will stop iteration and return true on the first assertion that passes the expectation. Otherwise, false will be returned and the final expect statement will fail.
The technique here is to catch the exceptions thrown from the assertions and convert them to false results:
const expected = {name: 'Mete', age: 19};
const myArray = [{ name: 'Mete', age: 19, phone: '123456' }];
const found = myArray.some(obj => {
try {
expect(obj).toEqual(expected);
return true;
}
catch {
return false;
}
});
expect(found).toBeTruthy();
With every array function, you can get the result:
const strictlyEquals = (arr, props) => arr.every(o=>Object.keys(o).every(i=>props.includes(i)));
console.log(strictlyEquals([{name:'Test', age: 20}], ['name', 'age']));
console.log(strictlyEquals([{name:'Test', age: 20, phone:'Test'}], ['name', 'age']));
If you have to match complete object then:
const strictlyEquals = (arr, props) => arr.every(o=> Object.keys(o).length===Object.keys(props).length && Object.keys(o).every(i=>o[i]===props[i]));
console.log(strictlyEquals([{name:'Test', age: 20},{name:'Test', age: 20}], {name:'Test', age: 20}));
Related
I'm trying to write a function that will be called with an array that has information on a person such as their name and then age. I need this function to grab all of the numbers only and then return them then added up. I've done some research and it seems filter and reduce are what I need to do this in the easiest way for a total beginner like me to do?
Apologies for any typos/wrong jargon as my dyslexia gets the better of me sometimes.
An example of what kind of array is being passed into the function when called;
{ name: 'Clarie', age: 22 },
{ name: 'Bobby', age: 30 },
{ name: 'Antonio', age: 40 },
Would return the total added numbers.
// returns 92
Why isn't the array I'm calling this function with working? Can you provide me a working example without the array being hardcoded like the other answers? - I'm passing in an array to the function. The main objective is to grab any number from the passed in array and add them together with an empty array returning 0.
function totalNums(person) {
person.reduce((a,b) => a + b, 0)
return person.age;
}
console.log(totalNums([]))
You need to save the result into a new variable then console.log() it like this
const arr= [{ name: 'Clarie', age: 22 },
{ name: 'Bobby', age: 30 },
{ name: 'Antonio', age: 40 },...
];
function totalNums(person) {
let res = person.reduce((a,b) => a + b.age, 0)
return res;
}
console.log(totalNums(arr));
and this is why it has to be like that
.reduce()
js methods like .map(), .filter(), .reduce() and some others, they return a new array, they don't modify the original array.
You can console.log(arr); and you will get this output:
[{ name: 'Clarie', age: 22 },
{ name: 'Bobby', age: 30 },
{ name: 'Antonio', age: 40 },...
];
Your original array unchanged even after running your function so in order to get the result you expect, you need to store it inside a new variable
You need to save the result of your reduce.
For example with array of numbers you would do:
function totalNums(person) {
let res = person.reduce((a,b) => a + b, 0)
return res;
}
console.log(totalNums([5,6,4]))
And for your example you would like to do something like this:
function totalNums(person) {
let res = person.reduce((a,b) => a + b.age, 0)
return res;
}
console.log(totalNums([
{ name: 'Clarie', age: 22 },
{ name: 'Bobby', age: 30 },
{ name: 'Antonio', age: 40 }
]))
function totalNums(person) {
person.reduce((a,b) => a + b, 0)
return person.age;
}
console.log(totalNums([]))
Talking about the function you have created it is incorrect because:
return person.age; Here you are passing an array to function and then accessing it like it's an object.
person.reduce((a,b) => a + b, 0) you can't add a and b because b is an object.
You are not storing value which reduce function will return.
Solution Below :
The reduce function always returns something It never makes changes in the original array.
function totalNums(persons) {
const totalAge = persons.reduce((total, person) => total + person.age, 0);
return totalAge;
}
const persons = [
{ name: "Clarie", age: 22 },
{ name: "Bobby", age: 30 },
{ name: "Antonio", age: 40 },
];
console.log(totalNums(persons));
You can replace total and person with a and b respectively in the above code snippet for your reference.
I have read several solutions to this problem here. When I try it, I continue to receive an error for the pop() method.
I have what is essentially a multidimensional array in javascript.
I am tasked with returning the array with the sensitive info removed (e.g. remove the SSN, in this example)
I thought I could use a foreach loop, and the pop() function to remove the last element of the child arrays, the SSN.
testing it using node on the commandline, the stdout is telling me that element.pop() is not a function. i've tried it with pop(), slice(), filter(), all with no success.
when running $> node filename.js
H:\Apache2\htdocs\test\filename.js:50
noppi[i] = element.pop();
^
TypeError: element.pop is not a function
let recs = [
{
ID: 1,
NAME: 'John',
EMAIL: 'john#example.com',
SSN: '123'
}, {
ID: 2,
NAME: 'Sally',
EMAIL: 'sally#example.com',
SSN: '456'
}, {
ID: 3,
NAME: 'Angie',
EMAIL: 'angie#example.com',
SSN: '789'
}
];
let i = 0;
let noppi = [];
recs.forEach(element => {
noppi[i] = element.pop();
i++;
});
console.log(noppi);
At the risk of sounding redundant, I'll briefly reiterate what the earlier answers have already stated.
The input data structure isn't a multi-dimensional array [ [ ... ], [ ... ] ] , it's an array of objects [ {...}, {...} ]. So you can't use Array methods like .pop() on the objects {...}.
Here's a simple one-liner that uses .forEach() and delete.
recs.forEach(obj => delete obj.SSN)
delete is an operator with one purpose: to remove an object's property like for example SSN: '123-45-6789'. Simple and perfect.
Note, .forEach() mutates the array, meaning that it's the original data being changed (see Minja's comment).
let recs = [
{
ID: 1,
NAME: 'John',
EMAIL: 'john#example.com',
SSN: '123'
}, {
ID: 2,
NAME: 'Sally',
EMAIL: 'sally#example.com',
SSN: '456'
}, {
ID: 3,
NAME: 'Angie',
EMAIL: 'angie#example.com',
SSN: '789'
}
];
recs.forEach(obj => delete obj.SSN);
console.log(recs)
Try this:
recs.forEach(element => {
noppi.push = element;
});
You are trying to use pop() on an object not an array
As per your need you need to remove SSN from your object, try below code it should work for you.
recs.forEach(element => {
const { SSN, ...rest } = element;
noppi.push(rest);
});
Here we are removing SSN from object and rest will push in noppi.
This question already has answers here:
How to filter object array based on attributes?
(21 answers)
Closed last year.
Below is the array with objects:
myArray:[
{"name":"Ram", "email":"ram#gmail.com", "userId":"HB000006"},
{"name":"Shyam", "email":"shyam23#gmail.com", "userId":"US000026"},
{"name":"John", "email":"john#gmail.com", "userId":"HB000011"},
{"name":"Bob", "email":"bob32#gmail.com", "userId":"US000106"}
]}
I tried this but I am not getting output:
item= myArray.filter(element => element.includes("US"));
I am new to Angular.
let filteredArray = myArray.filter(function (item){
return item.userId.substring(0,2).includes('US')
})
Console.log(filteredArray)
//Output
[ { name: 'Shyam', email: 'shyam23#gmail.com', userId: 'US000026' },
{ name: 'Bob', email: 'bob32#gmail.com', userId: 'US000106' } ]
As noted by #halfer - You need to filter on the property that you are interested in - in this case - 'userId' - you can do this by simply adding the property into the code you already had tried and it will log out the specified items - or alternatively - you can make a utility function that takes the array, property and target string as arguments and this will allo2w you to search / filter other arrays and by any property and target string .
These two options are shown below and both log out the same results.
const myArray = [
{"name":"Ram", "email":"ram#gmail.com", "userId":"HB000006"},
{"name":"Shyam", "email":"shyam23#gmail.com", "userId":"US000026"},
{"name":"John", "email":"john#gmail.com", "userId":"HB000011"},
{"name":"Bob", "email":"bob32#gmail.com", "userId":"US000106"}
]
// option 1 - direct filtering
const matchingItems = myArray.filter(element => element.userId.includes("US"));
console.log(matchingItems);
// gives - [ { name: 'Shyam', email: 'shyam23#gmail.com', userId: 'US000026' }, { name: 'Bob', email: 'bob32#gmail.com', userId: 'US000106' } ]
//option 2 - create a function that takes arguments and returns the matches
const matches = (arr, prop, str) => {
return arr.filter(element => element[prop].includes(str));
}
console.log(matches(myArray, 'userId', 'US'));
// gives - [ { name: 'Shyam', email: 'shyam23#gmail.com', userId: 'US000026' }, { name: 'Bob', email: 'bob32#gmail.com', userId: 'US000106' } ]
I know indexOf cannot be used in an array of objects, e.g. below.
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29}
]
console.log(objs.indexOf({ name: 'eliot', age: 29 })) // print -1
However, why DOM element array index can be traced by indexOf??
e.g. below
document.getElementById('slides').addEventListener('click', function (e) {
const nodes = document.querySelectorAll('#slides > .slide')
const newNodes = [...nodes]
console.log(newNodes);
console.log(newNodes.indexOf(e.target)) // can print out the index
})
Every time the interpreter comes across an object literal when it runs a line - for example:
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29} // this line
]
or
console.log(objs.indexOf(
{ name: 'eliot', age: 29 } // this line
))
It creates a new object with those properties. They aren't the same.
console.log(
// two separate objects, which happen to contain the same value
{ foo: 'foo' } === { foo: 'foo' }
);
In contrast, when looking up nodes in the DOM, the nodes are generally static - they don't change unless JavaScript runs which explicitly does so. The e.target refers to one such static node, and querySelectorAll returns a collection of such static nodes.
So they're ===, and .indexOf works.
Consider the following behavior:
const element = { foo: 1 };
const arr = [{ foo: 1 }, { foo: 2 }, element];
console.log(arr.indexOf({ foo: 1 })); // -1
console.log(arr.indexOf(element)); // 2
console.log(arr.indexOf(arr[0])); // 0
It clearly demonstrates how indexOf matches the reference to an object, rather than an object with the same keys and values.
For one { name: 'eliot', age: 29 } is a new object and hence not found in your array
So you can find an object if it is the actual same object
Otherwise look for it by the value of one or more of the entries
const objs = [
{name: 'darling', age: 28},
{name: 'eliot', age: 29}
]
const objToFind = objs[1]
console.log(objs.indexOf(objToFind))
// find the index by name
const idxOfEliot = objs.findIndex(({name}) => name==="eliot")
console.log(idxOfEliot)
I need to replace nested arrays inside a main array that have null values like lets say [null, null] or the nested arrays that are empty with a string value like "empty".
Saying that we have the following array:
array = [
{
id: 123,
name: 'Peter',
phone: [null, null],
addresses: [{ address1: 'Manchester, UK', address2: 'London, UK' }]
},
{
id: 124,
name: 'Sara',
phone: [],
addresses: [{ address1: 'London, UK', address2: 'Paris, FR' }]
}
];
We see that, the first array has phone: [null, null] and the second has it as []. What I need to do it to transform them into the following:
array = [
{
id: 123,
name: 'Peter',
phone: "empty",
addresses: [{ address1: 'Manchester, UK', address2: 'London, UK' }]
},
{
id: 124,
name: 'Sara',
phone: "empty",
addresses: [{ address1: 'London, UK', address2: 'Paris, FR' }]
}
];
This is an example, and each array might contain multiple nested arrays that have the same [null, null] or [].
I tried the following:
var filtered = this.array.map(subarray => subarray.filter(el => el != null));
from this Stack Overflow answer, but I've got an error saying:
Error: subarray.filter is not a function
Then I tried a second method using lodash's every() and isNull method and property but couldn't figure it out:
let props = [];
props = Array.from(new Set(this.array.flatMap(e => Object.keys(e), [])));
console.log(props)
for (const prop of props) {
this.array.forEach(e => {
if ((Array.isArray(e[prop]) || typeof(e[prop]) === 'object') && e[prop]._.every(_.isNull)) {
console.log(e)
}
});
}
I searched few questions on Stack Overflow but the structure of the arrays are like: [ [1, 2], [1,3]...] and not like my array's structure [{...}, {...}], so I tried some of the solution and got the same error of method 1 above.
Here is a stackblitz.
First loop through the array, and within each object, you can set the phone property:
for(const entry of array) {
const isEmpty = entry.phone.filter(p => p !== null).length === 0;
entry.phone = isEmpty ? 'empty' : entry.phone;
}
Caveat is that this edits your array. One concern about the premise of the question is that you are setting an array property to a string, which is not ideal.
Live Example: https://jsfiddle.net/michaschwab/9ze3p2or/3/, and here's your edited stackblitz: https://stackblitz.com/edit/null-nested-array-into-string-jwhfwn
If you want to not modify your array, this is a way to do it:
const modified = array.map(entry => {
return {...entry, // Copy previous values
phone: entry.phone.filter(p => p !== null).length === 0 ? 'empty' : entry.phone
};
});
Map the array, and use _.mapValues() on each object. For each values that is an array, and full of null values, return 'empty':
const array = [{"id":123,"name":"Peter","phone":[null,null],"addresses":[{"address1":"Manchester, UK","address2":"London, UK"}]},{"id":124,"name":"Sara","phone":[],"addresses":[{"address1":"London, UK","address2":"Paris, FR"}]}];
const result = array.map(o =>
_.mapValues(o, v => // map the values of the object
_.isArray(v) && v.every(_.isNull) ? 'empty' : v // if a value is an array, and all values are null return 'empty'
)
);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>