This question already has answers here:
What is the fastest or most elegant way to compute a set difference using Javascript arrays?
(14 answers)
Closed 9 years ago.
I have arrays like;
var john = { name: "John Smith", age: 23 };
var mary = { name: "Mary Key", age: 18 };
var bob = { name: "Bob-small", age: 6 };
var people = [john, mary, bob];
var john2 = { name: "John Smith", age: 23 };
var people2 = [john2];
What I would like to do is subtract people2 from people and get result;
[mary, bob];
How can I achieve this?
TIA
The difference of two sets, A and B, is defined as the set of all those elements of A which are not in B. If we implement it naively, computing the difference of two sets of sizes m and n respectively would take O(m * n) time. Not very efficient:
const john1 = { name: "John Smith", age: 23 };
const john2 = { name: "John Smith", age: 23 };
const mary = { name: "Mary Key", age: 18 };
const bob = { name: "Bob-small", age: 6 };
const people1 = [john1, mary, bob];
const people2 = [john2];
const eqPerson = (p, q) => p.name === q.name && p.age === q.age;
const result = people1.filter(p => people2.every(q => !eqPerson(p, q)));
console.log(result); // [mary, bob]
Fortunately, there's a faster way to compute the set difference for large sets using hashing.
const john1 = { name: "John Smith", age: 23 };
const john2 = { name: "John Smith", age: 23 };
const mary = { name: "Mary Key", age: 18 };
const bob = { name: "Bob-small", age: 6 };
const people1 = [john1, mary, bob];
const people2 = [john2];
const hashPerson = ({ name, age }) => `${name} ${age}`;
const hashSet = new Set(people2.map(hashPerson));
const result = people1.filter(p => !hashSet.has(hashPerson(p)));
console.log(result); // [mary, bob]
The advantage is that creating a hash set takes O(n) time and calculating the difference takes O(m) time. Hence in total it only takes O(m + n) time instead of O(m * n) time. In addition, you can reuse the hash set in the future.
Here is an easy solution:
var diff = people.filter(function(item) {
return !people2.some(function(test){
return test.name === item.name && test.age === item.age;
});
});
Make sure the function passed to people2.some correctly checks that the two objects are equal, since == would fail, as you have references to different objects with identical properties.
So, here goes the final running code. Copy , paste it and check for the output.It is first of all separating the strings (comma-separation). Then it is comparing them individually . Check this link: http://jsfiddle.net/DXRZ4/
<html>
<head>
<script>
var a = new Array(); var i=1; var people = new Array();
function fun() {
var str = "john,john,bob",
l = str.split(",");
for(i=0; i<3; i++) {
a[i] = l[i];
// this will show the comma separated strings
document.writeln(l[i]);
}
for(i=0; i<3; i++) {
t=a[i];
j=a[i+1];
if((a[i]==a[i+1]) || (a[i]==a[i+2])) {
// it will store the position of string which are same
p=i;
}
}
for(i=0; i<3; i++) {
if(p!=i){
document.writeln("Subtracted Strings are:-");
document.writeln(l[i]);
}
}
}
</script>
<body>
<input type="button" name="b1" onClick="fun()">
</body>
</html>
Related
I need to have a method which needs to check in the valid range between -064.000000 to -180.000000 and 142.000000 to 180.000000. The ranges object that I have looks like the following:
"ranges": {
"range1": {
"min": -180,
"max": -64
},
"range2": {
"min": 142,
"max": 180
}
}
So far, this is what I was able to complete but it doesn't seem to work right:
const mustBeInRangeInclusive = ({ userInput, ranges }) => {
let data_num = _.toNumber(userInput);
for (let i = 0; i < ranges.length; i++) {
return result = data_num >= ranges[i][0] && data_num <= ranges[i][1];
}
};
Can someone please help me complete this method to figure out what am I doing wrong?
Expected output:
-63 -> invalid
-64: valid
181 -> invalid
180: valid
141 -> invalid
142: valid
Few edits, as the question code keeps changing a bit.
First problem - accessing objects properties
If you have an array, you access its values by indeces.
let array = [1, 2, 3];
let firstItem = array[0];
let secondItem = array[1];
If you have object, you access its propeties by their names.
let someObject = { 'name': 'John', 'age': 21 };
let name = someObject.name;
name = someObject['name'];
If you have array of objects, you combine both methods.
let arrayOfObjects = [
{ 'name': 'John', 'age': 21 },
{ 'name': 'Sam', 'age': 23 }
]
let firstObjectsName = arrayOfObjects[0].name;
Second problem - exiting the loop on the first iteration
You call return statement as soon as you enter the loop making it impossible to enter the second iteration. You could store result of each iteration in the array and return it in the end.
const mustBeInRangeInclusive = ({ userInput, ranges }) => {
let results = [];
let data_num = _.toNumber(userInput);
for (let i = 0; i < ranges.length; i++) {
results.push(data_num >= ranges[i].min && data_num <= ranges[i].max);
}
return results;
};
This answer based upon the comment request of OP, and does not solve the issue if filters would be an object.
Assuming you can change the definition, using an array of filters would be a lot easier to work with than an object with filters. You can use the array every method to check if every elements matches a criteria. Use some to check if some (one or more) elements matches a criteria.
const userInput = document.getElementById("user-input");
const ranges = [ // <- changed definition to an array
{ "min": 0, "max": 100 },
{ "min": 50, "max": 150 },
];
userInput.addEventListener("change", () => {
const nr = parseInt(userInput.value, 10);
const coversNr = ({min, max}) => min <= nr && nr <= max;
const withinEveryRange = ranges.every(coversNr);
const withinSomeRange = ranges.some(coversNr);
console.log("withinEveryRange //=>", withinEveryRange);
console.log("withinSomeRange //=>", withinSomeRange );
});
<input id="user-input" type="number" />
I'm practicing my javascript and I've come across the following array.
const people = [
{
name: 'Carly',
yearOfBirth: 2018,
},
{
name: 'Ray',
yearOfBirth: 1962,
yearOfDeath: 2011
},
{
name: 'Jane',
yearOfBirth: 1912,
yearOfDeath: 1941
},
]
i'm trying to find the oldest person in the array but I keep getting the wrong person.here's my code
let findTheOldest = function(people) {
const oldest = people.sort((a,b) => (a.yearOfDeath - a.yearOfBirth) > (b.yearOfDeath - b.yearOfBirth) ? -1 : 1);
return oldest[0];
}
so it keeps saying that 'Carly' is the oldest person rather than 'Ray'? How would I go about it? note that 'Carly' has no yearOfDeath and therefore she is still alive.
You can use reduce, and use the current year for people without a death date:
const people = [{name:"Carly",yearOfBirth:2018},{name:"Ray",yearOfBirth:1962,yearOfDeath:2011},{name:"Jane",yearOfBirth:1912,yearOfDeath:1941}];
const findTheOldest = function(people) {
const thisYear = new Date().getFullYear();
return people.reduce((res, person) => {
const age = (person.yearOfDeath || thisYear) - person.yearOfBirth;
return age > res.age ? { person, age } : res;
}, { person: null, age: 0 }).person;
}
console.log(findTheOldest(people)); // Ray
As an Engineer, most proofs I study at college assume something and we work it out, then at the end of the proof, Maths will tell you if your assumption was right.
We'll assume we have a function called getAge(person) that has a signature as follows.
// this performance improvement of storing the year was suggested by #blex
let currentYear = new Date().getFullYear();
let getAge = (person) => {
return (person.yearOfDeath ? person.yearOfDeath : currentYear) - person.yearOfBirth
};
Basically, if the person doesn't have a .yearOfDeath, he's still alive and the current year 2020 at the time of writing this answer.
and we have a getOldest(people) that has a signature as follows.
let getOldest = people => {
/** Keep in mind that people can be mutated and you
need to avoid this here
*/
// An assumption. It can be right or wrong.
let oldest_person = people[0];
// The first method (slice) returns a shallow copy
// the second one (splice) removes the oldest_person
// removing it makes the loop count decrease by one. Nothing else.
// we don't need a deep copy, we won't alter the people.
people = (people.slice()).splice(1);
// You could save the getAge(oldest_person) in a variable
// instead of computing it each time
// I wanted this to be as readable as possible.
for (let person of people){
if (getAge(person) > getAge(oldest_person)){
// Congrats! we have a new older person!
oldest_person = person;
}
}
return oldest_person;
};
This has a worst-case time complexity of o(n).
For illustration, let's benchmark this.
let people = []
let init = () => {
let randomInteger = (min, max) => {
// return random integer between min, max, found this on stackoverflow
return Math.floor(Math.random() * (max - min + 1)) + min;
}
for (let i = 0; i < 10000000; i++){ // 10m person
let dateOfBirth = parseInt('19' + randomInteger(10,99));
let dateOfDeath = parseInt('20' + randomInteger(10, 99));
let person = {
name: `person_${i}`,
dateOfBirth, // same as dateOfBirth: dateOfBirth,
dateOfDeath, // same logic
}
people.push(person); // add it to people
}
}
init();
start = performance.now(); // time in millisecs
getOldest(people);
end = performance.now(); // time in millisecs after getting the oldest person
console.log((end - start ) * Math.pow(10, -3)) // time elapsed is around 0.2 secs.
To use sort to find the oldest, you need to include a default specifying the current year for people without a yearOfDeath. Below I've done this in a helper function called "age".
Using sort if your only purpose is to find a maximum can be inefficient though, particularly if you're dealing with a lot of data: try using reduce, as per other answer.
const people = [
{
name: "Carly",
yearOfBirth: 2018,
},
{
name: "Ray",
yearOfBirth: 1962,
yearOfDeath: 2011,
},
{
name: "Jane",
yearOfBirth: 1912,
yearOfDeath: 1941,
},
];
let findTheOldest = function (people) {
const age = (x) => (x.yearOfDeath || new Date().getFullYear()) - x.yearOfBirth;
const oldest = people.sort((a, b) =>
age(a) > age(b) ? -1 : 1
);
return oldest[0];
};
console.log(findTheOldest(people));
I’m having a lot of trouble learning how to use for loops to fill a new variable. As an example say if I have var year = [2010, 2000, 1992]; and var age = [];.
How would I use a for loop to fill in the age variable?
If this is a bad example, don’t use this. I just would like some help with understanding how to fill in empty arrays.
var names = ["Ace", "yoshi", "Lassie"];
var age = [25, 23, 5];
var u24 = [];
for (var i = 0; i < names.length; i++) {
if ([age] < 24) {
u24 += age[i]
console.log("hello " + names + " " + "you are" + age);
}
}
It is better to create objects that contain relevant data. Combining the name and age into a person object would help.
var persons = [
{
name: "Ace",
age: 25
},
{
name: "yoshi",
age: 23
},
{
name: "Lassie",
age: 5
}
];
var u24=[];
for (var i =0; i < persons.length; i++) {
var person = persons[i];
if(person.age < 24){
u24.push(person.age);
console.log("hello " + person.name + " " + "you are " + person.age);
}
}
console.log(u24);
But you can also use forEach like this:
var persons = [
{
name: "Ace",
age: 25
},
{
name: "yoshi",
age: 23
},
{
name: "Lassie",
age: 5
}
];
var u24=[];
persons.forEach(
function(person) {
if(person.age < 24){
u24.push(person.age);
console.log("hello " + person.name + " " + "you are " + person.age);
}
}
);
console.log(u24);
By making objects that include all relevant data your loops will never get out of sync. If you remove a person from the persons array then their name and age will go together.
UPDATE: Using a filter
var persons = [
{
name: "Ace",
age: 25
},
{
name: "yoshi",
age: 23
},
{
name: "Lassie",
age: 5
}
];
var youngPersons = persons.filter(
function(person) {
return (person.age < 24);
}
);
console.log(youngPersons);
Or using an ES6 Arrow Function
var persons = [
{ name: "Ace", age: 25 },
{ name: "yoshi", age: 23 },
{ name: "Lassie", age: 5 }
];
var youngPersons = persons.filter((person) => person.age < 24);
console.log(youngPersons);
This provides back an array of the persons that match your Age under 24 criteria.
If all you want to do is fill in the age array with a loop, you can try this:
let years = [2010, 2000, 1992],
age = [],
d = new Date().getFullYear();
years.forEach(year => age.push(d - year));
console.log(age);
As regards the relationship between age and names, I think Intervalia has explained that.
A working version.
Please compare it with your code to see the differences. Arrays always got me when I was starting out, and the syntax with different across languages, despite the reuse of bracket symbols.. AutoIt language still trips me up :P
var names = ["Ace", "yoshi", "Lassie"];
var age = [25, 23, 5];
//Use array.push() to append values
var u24 = [];
//variable i counts up to names.length
//because i++ means 'add one' to i
for (var i = 0; i < names.length; i++) {
//if ([age] < 24) {u24 += age[i];
//age at the count 'i' (which is
//counting)
//is achieved by array[at_index]
if (age[i] < 24) {
u24.push(age[i]); //add it to the end
console.log("Hello " + names[i] +
", you are " + age[i]);
}
}
The following fixed expression and values need to be changed to dynamic:
function update(args) {
const params = {
UpdateExpression: 'set firstName = :firstName, lastName = :lastName, sex = :sex',
// values map to UpdateExpression
ExpressionAttributeValues: {
':firstName': args.firstName,
':lastName': args.lastName,
':sex': args.sex
}
};
// db specific code, not relevant
}
It currently only works with fixed arguments, for example:
update({firstName: 'Joe', lastName: 'Doe', sex: 'M'}
I need the UpdateExpression and ExpressionAttributeValues to dynamically change with more or less arguments, e.g:
args also has height: 190
add height = :height to UpdateExpression (and comma)
add ':height': args.height to ExpressionAttributeValues
How should I parse that expression and how should I deal with the commas?
What I've got:
Expression string generation seems awkward and I have problems with commas.
const args = {
id: 'id-123',
firstName: 'John',
lastName: 'Doe',
updated: 1491111234
};
function update(args) {
console.log('===');
expression = 'set ';
values = {};
for (let variable in args) {
// expression string
let str = `${variable} = :${variable}, `;
console.log(str);
expression = expression.concat(str);
// values object
values[`:${variable}`] = args[variable];
}
console.log('---');
console.log('UpdateExpression:', expression);
console.log('ExpressionAttributeValues:', values);
console.log('===');
}
update(args);
You just need a way of determining when to add a comma, which should be every time except for the last. So you could use Object.keys to get the keys of the object and do a standard for loop to check if it's the last one.
const args = {
id: 'id-123',
firstName: 'John',
lastName: 'Doe',
updated: 1491111234
};
function update(args) {
console.log('===');
expression = 'set ';
values = {};
const keys = Object.keys(args);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = args[key];
const str = `${key} = :${key}`;
console.log(str);
expression += str;
values[`:${key}`] = value;
if (i < keys.length - 1) {
expression += ', ';
}
}
console.log('---');
console.log('UpdateExpression:', expression);
console.log('ExpressionAttributeValues:', values);
console.log('===');
}
update(args);
I have a function called insert which takes two parameters (name and telnumber).
When I call this function I want to add to an associative array.
So for example, when I do the following:
insert("John", "999");
insert("Adam", "5433");
I want to it so be stored like this:
[0]
{
name: John, number: 999
}
[1]
{
name: Adam, number: 5433
}
Something like this should do the trick:
var arr = [];
function insert(name, number) {
arr.push({
name: name,
number: number
});
}
I would use something like this;
var contacts = [];
var addContact = function(name, phone) {
contacts.push({ name: name, phone: phone });
};
// Usage
addContact('John', '999');
addContact('Adam', '5433');
I don’t think you should try to parse the phone number as an integer as it could contain white-spaces, plus signs (+) and maybe even start with a zero (0).
var users = [];
users.push({name: "John", number: "999"});
users.push({name: "Adam", number: "5433"});
If you want you can add your function to Array.prototype.
Array.prototype.insert = function( key, val ) {
var obj = {};
obj[ key ] = val;
this.push( obj );
return this;
};
And use it like this.
var my_array = [].insert("John", "999")
.insert("Adam", "5433")
.insert("yowza", "1");
[
0: {"John":"999"},
1: {"Adam":"5433"},
2: {"yowza":"1"}
]
I will assume you're using some array reference with insert:
var arr;
function insert(na, nu) {
nu = Number(nu) || 0;
//alternatively
nu = parseInt(nu, 10);
arr.push({ name: na, number: nu });
}
arr = [];
insert("John", "999");
insert("Adam", "5433");
There is no such term as an "associative array" in JavaScript, though you can use following:
var list = [];
function insert(name, number) {
list.push({
name: name,
number: number
});
}