Removing All Elements with feature X from Array, except the latest - javascript

I have the following object:
[
{ name: "Peter", id: 25, job: "carpenter" },
{ name: "Peter", id: 25, job: "shelf maker" },
{ name: "John", no: 20, job: "student" },
{ name: "John", id: 20, job: "university student" },
{ name: "John", id: 20, job: "student at uni still" },
{ name: "Jack", id: 20, job: "university student" }
]
I would like to go through this array and whenever name and id are identical I would like to only keep one entry, namely the one appearing the latest in the array, and discard all the rest. How would I do this?
I have tried
for(let i=0;i<people.length;i++) {
const person = people[i];
const result = people.filter(person => person.id === id && person.name === name);
people[i] = person;
}
... but this doesn't work. Any ideas what I'm doing wrong? How would you approach this?

You could use reduceRight to build new array starting the iteration from the end of the array, and also Map as accumulator value to store key - value pairs.
const data = [{"name":"Peter","id":25,"job":"carpenter"},{"name":"Peter","id":25,"job":"shelf maker"},{"name":"John","no":20,"job":"student"},{"name":"John","id":20,"job":"university student"},{"name":"John","id":20,"job":"student at uni still"},{"name":"Jack","id":20,"job":"university student"}]
const map = data.reduceRight((r, e) => {
const key = `${e.name}|${e.id}`;
if (!r.has(key)) r.set(key, e);
return r;
}, new Map);
const uniq = [...map.values()];
console.log(uniq)

I'd reduce into an object, whose keys are the ID and name put together, and whose values are the latest object with a particular ID and name found so far, and then get the object's values:
const input=[{name:"Peter",id:25,job:"carpenter"},{name:"Peter",id:25,job:"shelf maker"},{name:"John",no:20,job:"student"},{name:"John",id:20,job:"university student"},{name:"John",id:20,job:"student at uni still"},{name:"Jack",id:20,job:"university student"}];
const output = Object.values(
input.reduce((a, obj) => {
const { name, id } = obj;
const key = `${name}_${id}`;
a[key] = obj;
return a;
}, {})
);
console.log(output);
Computational complexity is O(N), since there are no nested loops.

You can use reduceRight with a Map to check if the object already exists in the accumulator - if it does, ignore it, else push the new object, and new key + index pair to the map:
const arr = [
{ name: "Peter", id: 25, job: "carpenter" },
{ name: "Peter", id: 25, job: "shelf maker" },
{ name: "John", no: 20, job: "student" },
{ name: "John", id: 20, job: "university student" },
{ name: "John", id: 20, job: "student at uni still" },
{ name: "Jack", id: 20, job: "university student" }
];
const m = new Map([])
const output = arr.reduceRight((a, o, i) => (m.has(o.name + o.id) || a.push(o) && m.set(o.name + o.id, i), a), [])
console.log(output)
However, a regular for loop is the fastest solution here:
const arr = [
{ name: "Peter", id: 25, job: "carpenter" },
{ name: "Peter", id: 25, job: "shelf maker" },
{ name: "John", no: 20, job: "student" },
{ name: "John", id: 20, job: "university student" },
{ name: "John", id: 20, job: "student at uni still" },
{ name: "Jack", id: 20, job: "university student" }
];
const m = new Map([])
const out = []
for (var i = arr.length - 1; i >= 0; i--) {
const o = arr[i]
if (!m.has(o.name + o.id)) {
out.push(o)
m.set(o.name + o.id, i)
}
}
console.log(out)
See the performance test here with a larger, shuffled array.

Related

Remove duplicates from an array of objects based on time created

I have an array of objects and there are some duplicate objects
const data = [
{
"id": "1011",
"name": "abc",
"Dob": "3/2/11",
"timeCreated": "16:03:41"
},
{
"id": "1012",
"name": "xys",
"Dob": "6/5/12",
"timeCreated": "01:05:21"
},
{
"id": "1011",
"name": "xyz",
"Dob": "3/2/11",
"timeCreated": "17:03:41"
},
{
"id": "1011",
"name": "xyz",
"Dob": "3/2/11",
"timeCreated": "15:03:41"
}
]
I am removing duplicates in the array by using some()
let arr = [];
data.forEach(obj => {
if (!arr .some(o => o.id === obj.id)) {
arr.push({ ...obj})
}
});
I need help filtering it and only keeping the latest object based off of "timeCreated"
so the data looks something like this:
{
"id": "1012",
"name": "xys",
"Dob": "6/5/12",
"timeCreated": "01:05:21"
},
{
"id": "1011",
"name": "xyz",
"Dob": "3/2/11",
"timeCreated": "17:03:41"
},
]
you can do that :
const data =
[ { id: '1011', name: 'abc', Dob: '3/2/11', timeCreated: '16:03:41' }
, { id: '1012', name: 'xys', Dob: '6/5/12', timeCreated: '01:05:21' }
, { id: '1011', name: 'xyz', Dob: '3/2/11', timeCreated: '17:03:41' }
, { id: '1011', name: 'xyz', Dob: '3/2/11', timeCreated: '15:03:41' }
]
const arr = data.reduce((result,obj)=>
{
let row = result.find(x=>x.id===obj.id)
if (!row)
result.push({...obj})
else if (row.timeCreated < obj.timeCreated)
Object.assign(row,obj)
return result
},[])
console.log( arr )
.as-console-wrapper {max-height: 100%!important;top:0 }
Late to the party, but here's a shorter, maybe faster, more efficient solution that just involves a sort and filter operation
let tmp=[], arr = data.sort((a, b) => +b.timeCreated.replaceAll(':', '') - +a.timeCreated.replaceAll(':', ''))
.filter(o => (!tmp.includes(o.id) && tmp.push(o.id)));
How it works: Pretty simply actually. It first sorts the array by timeCreated descending. It does this by (on the fly) transforming the 'HH:MM:SS' string into the number HHMMSS (+b.timeCreated.replaceAll(':', '')), then comparing. Then it takes the sorted array and filters it through the temporary array tmp, which stores ids each iteration - and if the id is already in there (and we know that is the latest according to the timeCreated) we filter it out. This is all handled by the wonderfully simple ternary: .filter(o => (!tmp.includes(o.id) && tmp.push(o.id)), which says if we've already seen that id, return false, otherwise make a note of it
Why it's cool - For most use cases (small data sets), there isn't a significant difference between functional iterators like map, reduce, forEach, filter, sort - however this is thinking out of the box. Rather than build datasets and reduce them down, this smartly chops it to size first - using only 2 operations.
const data = [{ id: '1011', name: 'abc', Dob: '3/2/11', timeCreated: '16:03:41' }, { id: '1012', name: 'xys', Dob: '6/5/12', timeCreated: '01:05:21' }, { id: '1011', name: 'xyz', Dob: '3/2/11', timeCreated: '17:03:41' }, { id: '1011', name: 'xyz', Dob: '3/2/11', timeCreated: '15:03:41' }]
let tmp=[], arr = data.sort((a, b) => +b.timeCreated.replaceAll(':', '') - +a.timeCreated.replaceAll(':', '')).filter(o => (!tmp.includes(o.id) && tmp.push(o.id)));
console.log(arr)

Filter array and reset the index too

I have an array of objects like this:
const data = [
{
name: "Peter",
age: 20,
nationality: "American",
index: 0
},
{
name: "David",
age: 25,
nationality: "English",
index: 1
},
{
name: "Gabriel",
age: 23,
nationality: "Spanish",
index: 2
},
{
name: "Kate",
age: 22,
nationality: "English",
index: 3
},
];
If I want to return a new array with only the people with English nationality I'd use filter, like this:
let englishPerson = data.filter(el => el.nationality === 'English');
console.log(englishPerson);
And this will log the following:
> Array [Object { name: "David", age: 25, nationality: "English", index: 1 }, Object { name: "Kate", age: 22, nationality: "English", index: 3 }]
But I would like to reset the index after the data is filtered, so the first object in the new filtered array should have an index of 0, second an index of 1 and so on. In this case David has an index of 1, because it kept the same index from the original data.
You could filter followed by map, but it would be better to do it in one go with reduce - if the item passes the test, add it to the accumulator, with the index of the accumulator's current length:
const data=[{name:"Peter",age:20,nationality:"American",index:0},{name:"David",age:25,nationality:"English",index:1},{name:"Gabriel",age:23,nationality:"Spanish",index:2},{name:"Kate",age:22,nationality:"English",index:3},]
console.log(
data.reduce((a, item) => {
if (item.nationality === 'English') {
a.push({
...item,
index: a.length
});
}
return a;
}, [])
);
You can use Array.prototype.map() to modify the index property of the filtered result:
const data = [
{
name: "Peter",
age: 20,
nationality: "American",
index: 0
},
{
name: "David",
age: 25,
nationality: "English",
index: 1
},
{
name: "Gabriel",
age: 23,
nationality: "Spanish",
index: 2
},
{
name: "Kate",
age: 22,
nationality: "English",
index: 3
},
];
let i=0;
let englishPerson = data.filter(el => el.nationality === 'English').map(el => {
el.index = i; i++;
return el;
});
console.log(englishPerson);
Updated answer based on your comments:
const data = [
{
name: "Peter",
age: 20,
nationality: "American",
index: 0
},
{
name: "David",
age: 25,
nationality: "English",
index: 1
},
{
name: "Gabriel",
age: 23,
nationality: "Spanish",
index: 2
},
{
name: "Kate",
age: 22,
nationality: "English",
index: 3
},
];
let i=0;
let englishPerson = data.filter(el => el.nationality === 'English').map(el => {
if(el.hasOwnProperty('index')){
el.index = i; i++;
}
return el;
});
console.log(englishPerson);
If you want use your solution, with the slightest change, you can try this:
var c=0;
var englishPerson=data.filter(function(el){
return el.nationality=="English" && (el.index=c++)>-1;
});
console.log(englishPerson);
Do this:
const data = [
{
name: "Peter",
age: 20,
nationality: "American",
index: 0
},
{
name: "David",
age: 25,
nationality: "English",
index: 1
},
{
name: "Gabriel",
age: 23,
nationality: "Spanish",
index: 2
},
{
name: "Kate",
age: 22,
nationality: "English",
index: 3
},
];
data.filter(el => Object.values(el).includes('English')).forEach((obj, i) => {obj.index = i; console.log(obj)})
If you want to go with map, then just replace forEachwith map!
You can use from filters.tags.data.map(f => f.Key).toString() replace filter.
Where you use from the map, that index been reset.
For example:
$scope.ret = function(){
data: url,
method: post,
retuen filters.tags.data.map(f => f.Key).toString();
}

Return array of matching string in javascript

let data = [{
name: "John"
school: ["def","abc"]
},
{
name: "Lily"
school: "xyz"
}, {
name: "Rose"
school: "abc"
}]
I wanna return object which has school=="abc"`. i had try array.includes("abc). but it doesnt give expected output. it only return object which school that have only "abc" (output: Rose-abc). John should be included too
You could use
Array#filter for getting a new array with only some items of the array,
destructuring (assignment) for getting only a single property of an object,
a comparison with the wanted value, by checking if school is an array and if not make an array and take Array#includes for the check.
This is the return value of the arrow function.
var array = [{ name: "John", school: ["def", "abc"] }, { name: "Lily", school: "xyz" }, { name: "Rose", school: "abc" }],
result = array.filter(({ school }) => (Array.isArray(school) ? school : [school]).includes('abc'))
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
arr = [{
name: "John",
school: "abc"
}, {
name: "Lily",
school: "xyz"
}, {
name: "Rose",
school: "abc"
}]
a = arr.filter((x) => {
if (x.school == 'abc')
return x
})
console.log(a)
JSFiddle
var arr = [];
arr.push({ name: "John", school:"abc" });
arr.push({ name: "Lily", school:"xyz" });
arr.push({ name: "Rose", school:"abc" });
var temp = [];
arr.forEach(function(el) {
if(el.school == "abc")
temp.push(el);
});
console.log(temp);
arr1 = [];
var arr1= array1.concat(arr2);
var index = arr1.findIndex(x => x.school== "abc");
if ( index != -1 ){
console.log(" found")
}
first of all concat the array of two arrays and store arr1
then find index of the arr1 which of that value abc if that value is not found
it will return -1 based on this you can value is present or not
Say your array is like,
var arr=[ { name: "John" school:"abc" },
{ name: "Lily" school:"xyz" },
{ name: "Rose" school:"abc" }
]
then you can get the object like,
var item = arr.filter((obj)=>obj.school==="abc");
now, item will have the object you need
#mr ukta, if I'm understood correctly, it might help you.
var students = [
{
name: "John",
school:["abc", "def"]
},
{
name: "John2",
school:"Boom"
},
{
name: "John3",
school:"abc"
},
];
var abcStudents = students.filter(({ school }) => (Array.isArray(school) ? school : [school]).includes('abc'))
console.log(abcStudents);

Filter an array of objects returns empty array

I hae the following array of objects:
arr = [
{
name: "john",
age: 24,
gender: "male"
},
{
name: "jane",
age: 27,
gender: "female"
},
{
name: "joe",
age: 29,
gender: "male"
}
]
I'm trying to filter the name and age property into a new array. I tried this:
const newFields = arr.filter((item) => {
return (
item.name && item.age
);
});
But for some reason newFields returns an empty array.
Instead of .filter() use .map()
const arr = [
{
name: "john",
age: 24,
gender: "male"
},
{
name: "jane",
age: 27,
gender: "female"
},
{
name: "joe",
age: 29,
gender: "male"
}
];
const newFields = arr.map(item => {
return {
name: item.name,
age: item.age
}
});
console.log(newFields)
This can be a one liner too with the arrow function and Parameter Context Matching
const newFields = arr.map(({ name, age }) => ({ name, age }));
Your solution's result can not be empty but the original array, because in this case the return value of your filter function will be always true (every item has the name and age property) which doesn't filter anything from the original arr variable.
If you are creating a new array with a subset of the original - then you can iterate over the array and push the desired elements into its own array.
That said if all you are trying to get is the name and age of the existing array - why do you need to create a separate array? - you can just iterate over the original array and reference only the desired values from each item.
let arr = [
{
name: "john",
age: 24,
gender: "male"
},
{
name: "jane",
age: 27,
gender: "female"
},
{
name: "joe",
age: 29,
gender: "male"
}
]
let newArr = [];
arr.forEach(function(item) {
newArr.push({name:item.name, age:item.age})
});
console.log(newArr); // gives [{"name": "john","age": 24},{"name": "jane","age": 27},{"name": "joe","age": 29}]

retrieving the elements from array whose elements are objects in javascript

i have two arrays variables in which each element is an object having some properties like this :
var employees = [{
name: 'Jack',
empId: 0,
age: 25,
orgId: 1
}, {
name: 'Lucifer',
empId: 1,
age: 35,
orgId: 2
}, {
name: 'Adam',
empId: 3,
age: 46,
orgId: 1
}, {
name: 'Eve',
empId: 4,
age: 30,
orgId: 3
}];
and the second variable is
var companies= [{
name: 'Microsoft',
id: 1,
employees: [5 , 9]
}, {
name: 'Google',
id: 2,
employees: [1]
}, {
name: 'LinkedIn',
id: 3,
employees: [10]
}];
so now i want that when i give a company name (for example: Google),then it will return the employee details. i want to do it by using filter()/reduce() method, but i am not able to do it . Help needed .. thank you
If the employee orgId is the same as the company id you can use filter; one to get the id, and then another to grab the employees associated with that id. The function returns an array of employees.
function getEmployees(company) {
var id = companies.filter(function (el) {
return el.name === company;
})[0].id;
return employee.filter(function (el) {
return el.orgId === id;
});
}
getEmployees('Microsoft');
OUTPUT
[
{
"name": "Jack",
"empId": 0,
"age": 25,
"orgId": 1
},
{
"name": "Adam",
"empId": 3,
"age": 46,
"orgId": 1
}
]
DEMO
You can do that with a forEach loop and checking the name:
var Employees = []; //initialize
companies.forEach(function(company) {
if (company.name == "Microsoft"){
Employees = company.employees;
//whatever you like
Employees.forEach(function(id){
alert(id);
});
}
});
Working JsFiddle: https://jsfiddle.net/sapyt777/
Trying to brush-up my skills. So I'm pretty sure it works but I don't know if it's a good way to achieve what you want!
var input = "Microsoft";
for(company in companies) {
if(companies[company].name === input) {
for(emp in employee) {
if(companies[company].id === employee[emp].orgId)
{
console.log(employee[emp]);
}
}
}
}

Categories