Select the latest unique object from array of objects : Javascript - javascript

I am having array of objects with the following structure
const arr = [
{ id: 0, name: "abc", userid: "0", lastseen: 1645079273000 },
{ id: 3, name: "pqr", userid: "456", lastseen: 1645079273008 },
{ id: 1, name: "lmn", userid: "123", lastseen: 1645079273001 },
{ id: 3, name: "pqr", userid: "456", lastseen: 1645079273002 },
{ id: 4, name: "xyz", userid: "123", lastseen: 1645079273003 },
];
I want to return the objects where userid that is unique with latest entry should be returned. Ideally I might get userId as string 0. so to remove that added a filter clause
My approach:
const result = [...new Map(arr.filter(node=>node.userid!=="0").map(node=>[node.userid, node])).values()];
Output expected
[
{ id: 4, name: "xyz", userid: "123", lastseen: 1645079273003 },
{ id: 3, name: "pqr", userid: "456", lastseen: 1645079273008 },
]

Array.reduce implementation.
Logic
Run a loop on tha array using Array.reduce.
Create an object and update the value against key userId.
Produce the Array.values of this generated object. This will give you the unique list of values.
const arr = [
{ id: 0, name: "abc", userid: "0", lastseen: 1645079273000 },
{ id: 3, name: "pqr", userid: "456", lastseen: 1645079273008 },
{ id: 1, name: "lmn", userid: "123", lastseen: 1645079273001 },
{ id: 3, name: "pqr", userid: "456", lastseen: 1645079273002 },
{ id: 4, name: "xyz", userid: "123", lastseen: 1645079273003 },
];
const result = Object.values(arr.reduce((acc, curr) => {
if (curr.userid !== "0") {
acc[curr.userid] = acc[curr.userid] && acc[curr.userid].lastseen ?
acc[curr.userid].lastseen < curr.lastseen ? curr : acc[curr.userid] : curr;
}
return acc;
}, {}));
console.log(result)

Related

Please help to Convert Array to Object

I need to convert an array with only ids and an object with Id & Name need to find object array element from the object and create new Object
App.js:
["111","114","117']
Object:
[
{ id: "111", Name: "Jerry" },
{ id: "112", Name: "Tom" },
{ id: "113", Name: "Mouse" },
{ id: "114", Name: "Minny" },
{ id: "115", Name: "Mayavi" },
{ id: "116", Name: "Kuttoosan" },
{ id: "117", Name: "Raju" }
];
Result Need:
[
{ id: "111", Name: "Jerry" },
{ id: "114", Name: "Minny" },
{ id: "117", Name: "Raju" }
];
const array = ["111", "114", "117"];
const object = [
{ id: "111", Name: "Jerry" },
{ id: "112", Name: "Tom" },
{ id: "113", Name: "Mouse" },
{ id: "114", Name: "Minny" },
{ id: "115", Name: "Mayavi" },
{ id: "116", Name: "Kuttoosan" },
{ id: "117", Name: "Raju" }
];
const result = object.filter(o => array.includes(o.id));
This should give you the result you want, pay attention that what you called object actually is an array of objects, as far as i understood you want keep only the object with an id contained in the first array, so as i shown just filter them
I think you can just use .filter() to achieve the same result.
const targetIds = ["111","114","117"];
const nameObjects = [{id:"111", Name:"Jerry"}, {id:"112", Name:"Tom"}, {id:"113", Name:"Mouse"}, {id:"114", Name:"Minny"}, {id:"115", Name:"Mayavi"}, {id:"116", Name:"Kuttoosan"}, {id:"117", Name:"Raju"}];
const filtered = nameObjects.filter((obj) => targetIds.indexOf(obj.id) !== -1);
// Which should give the result you need
// [{id:"111", Name:"Jerry"}, {id:"114", Name:"Minny"},{id:"117", Name:"Raju"}]
Use the .filter() to get the result
const ids = ["111", "114", "117"];
const nameids = [
{ id: "111", Name: "Jerry" },
{ id: "112", Name: "Tom" },
{ id: "113", Name: "Mouse" },
{ id: "114", Name: "Minny" },
{ id: "115", Name: "Mayavi" },
{ id: "116", Name: "Kuttoosan" },
{ id: "117", Name: "Raju" }
];
const result = nameids.filter(res => ids.includes(res.id));
console.log(result);

How to map array of objects and return them as a new array of objects sorted in "categories" [duplicate]

This question already has answers here:
How can I group an array of objects by key?
(32 answers)
Closed 1 year ago.
I'm building my first react-native app and I want to show user some screen of his "followers" separated by categories but to do it I think I have to create new array of objects from this what I have.
I have array of objects like this:
followers: [
{
userId: 2,
userName: "Abigail",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
},
{
userId: 3,
userName: "John",
followDate: "1980-04-09T10:15:30+07:00",
category: "B"
},
{
userId: 4,
userName: "Bob",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
},
{
userId: 5,
userName: "Martha",
followDate: "1980-04-09T10:15:30+07:00",
category: "B"
}
]
And now I would like to render these followers to user separated by categories that are in followers array objects. So I think I need to create new array of objects and I think it should looks like this:
followersSortedByCategory = [
{
category: "A",
followers: [
{
userId: 2,
userName: "Abigail",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
},
{
userId: 4,
userName: "Bob",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
}
]
},
{
category: "B",
followers: [
{
userId: 3,
userName: "John",
followDate: "1980-04-09T10:15:30+07:00",
category: "B"
},
{
userId: 5,
userName: "Martha",
followDate: "1980-04-09T10:15:30+07:00",
category: "B"
}
]
}
]
I'm not sure how to do this without many "if's". I think there is a good way to do it using map, filter etc. functions but I don't know how.
Shorter solution:
const followers = [
{
userId: 2,
userName: "Abigail",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
},
{
userId: 3,
userName: "John",
followDate: "1980-04-09T10:15:30+07:00",
category: "B"
},
{
userId: 4,
userName: "Bob",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
},
{
userId: 5,
userName: "Martha",
followDate: "1980-04-09T10:15:30+07:00",
category: "B"
}
]
function formatFollowers(followers){
const categories = [...new Set(followers.map(follower=> follower.category))]
return categories.reduce((acc, category)=> {
const _followers = followers.filter(follower=> follower.category === category)
return [...acc, {category: category, followers: _followers}]
}, [])
}
console.log(formatFollowers(followers))
EDIT: on #Kinglish's request, here is the explanation of the code:
a) I loop over the followers array and only keep the category names. I also make sure to remove all potential duplicates thanks to new Set(). The result will look like this: const categories = ["A", "B"].
b) I loop over the categories array. For each category, I fetch all matching followers. Then, I push the formatted result in the final array.
c) output is properly formatted.
const followers = [
{ userId: 2, userName: "Abigail", followDate: "1980-04-09T10:15:30+07:00", category: "A" },
{ userId: 3, userName: "John", followDate: "1980-04-09T10:15:30+07:00", category: "B" },
{ userId: 4, userName: "Bob", followDate: "1980-04-09T10:15:30+07:00", category: "A" },
{ userId: 5, userName: "Martha", followDate: "1980-04-09T10:15:30+07:00", category: "B" }
];
const followersSortedByCategory = [...
// iterate over followers array while updating categoryMap (category-records)
followers.reduce((categoryMap, follower) => {
const { category } = follower;
// get record of follower's category if exists
const categoryRecord = categoryMap.get(category);
if(!categoryRecord) { // if not, add new pair
categoryMap.set(category, { category, followers: [follower] });
} else { // else update the record's followers array
categoryRecord.followers.push(follower);
}
return categoryMap;
}, new Map)
// return grouped list by categories
.values()
];
console.log(followersSortedByCategory);
All these answers probably will use reduce, and they're all probably as good as each other. But they all use subtly different approaches to the data iteration. This approach uses object keys
let categories = Object.values(
// the final output will be an array
data.followers.reduce( (b,a) => {
// we iterate the followers and build the accumulator object
if (b.hasOwnProperty(a.category)) b[a.category].followers.push(a)
// if our accumulating object already has the category key, add to the follower array
else b[a.category] = {category: a.category, followers: [a]}
// otherwise create the object key and initialize it with the first follower
return b
},{}))
let data = {
followers :[
{ userId: 2, userName: "Abigail", followDate: "1980-04-09T10:15:30+07:00", category: "A" },
{ userId: 3, userName: "John", followDate: "1980-04-09T10:15:30+07:00", category: "B" },
{ userId: 4, userName: "Bob", followDate: "1980-04-09T10:15:30+07:00", category: "A" },
{ userId: 5, userName: "Martha", followDate: "1980-04-09T10:15:30+07:00", category: "B" }
]}
let categories = Object.values(data.followers.reduce( (b,a) => {
if (b.hasOwnProperty(a.category)) b[a.category].followers.push(a)
else b[a.category] = {category: a.category, followers: [a]}
return b
},{}))
console.log(categories)
You can use lodash for this
const sortFollowers = ((followers) => {
const grouped = _.groupBy(followers, 'category');
console.log(grouped);
});
const followers = [{ userId: 2, userName: "Abigail", followDate: "1980-04-09T10:15:30+07:00", category: "A" }, { userId: 3, userName: "John", followDate: "1980-04-09T10:15:30+07:00", category: "A" }, { userId: 4, userName: "Bob", followDate: "1980-04-09T10:15:30+07:00", category: "A" }, { userId: 5, userName: "Martha", followDate: "1980-04-09T10:15:30+07:00", category: "B" }];
sortFollowers(followers);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
import "./styles.css";
import { _ } from "lodash";
const sortFollowers = ((followers) => {
const grouped = _.groupBy(followers, 'category');
console.log(grouped);
});
export default function App() {
const followers = [
{
userId: 2,
userName: "Abigail",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
},
{
userId: 3,
userName: "John",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
},
{
userId: 4,
userName: "Bob",
followDate: "1980-04-09T10:15:30+07:00",
category: "A"
},
{
userId: 5,
userName: "Martha",
followDate: "1980-04-09T10:15:30+07:00",
category: "B"
}
];
sortFollowers(followers);
}

How to first combine properties of an object then remove the duplicates in an array of objects using Javascript

I have an array of objects here:
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test2", quantity:1 },
{ id: 2, name: "test3", quantity:1 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:1 },
{ id: 5, name: "test7", quantity:1 },
{ id: 6, name: "test8", quantity:1 }
];
I want to add quantities of the duplicate objects together before removing them
So the result is:
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test3", quantity:2 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:2 },
{ id: 6, name: "test8", quantity:1 }
];
I have seen variations of it done removing duplicates using map or reduce but I haven't seen anything that can what I want to accomplish in an eloquent way without using too many loops.
I have been thinking about how to best accomplish this all day and haven't found anything, any help would be appreciated
You can use reduce with an object to store the element with each id.
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test2", quantity:1 },
{ id: 2, name: "test3", quantity:1 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:1 },
{ id: 5, name: "test7", quantity:1 },
{ id: 6, name: "test8", quantity:1 }
];
const res = Object.values(
arr.reduce((acc,curr)=>{
acc[curr.id] = acc[curr.id] || {...curr, quantity: 0};
acc[curr.id].quantity += curr.quantity;
return acc;
}, {})
);
console.log(res);
const arr = [
{ id: 1, name: "test1", quantity: 1 },
{ id: 2, name: "test2", quantity: 1 },
{ id: 2, name: "test3", quantity: 1 },
{ id: 3, name: "test4", quantity: 1 },
{ id: 4, name: "test5", quantity: 1 },
{ id: 5, name: "test6", quantity: 1 },
{ id: 5, name: "test7", quantity: 1 },
{ id: 6, name: "test8", quantity: 1 }
];
var result = arr.reduce(function (r, a) {
r[a.id] = r[a.id] || { id: a.id, quantity: 0, name: a.name };
r[a.id].quantity += a.quantity;
return r;
}, Object.create(null));
console.log(JSON.stringify(result));
Using forEach loop and build object with aggregated quantity count.
const convert = (arr) => {
const res = {};
arr.forEach(({ id, ...rest }) =>
res[id] ? (res[id].quantity += 1) : (res[id] = { id, ...rest })
);
return Object.values(res);
};
const arr = [
{ id: 1, name: "test1", quantity: 1 },
{ id: 2, name: "test2", quantity: 1 },
{ id: 2, name: "test3", quantity: 1 },
{ id: 3, name: "test4", quantity: 1 },
{ id: 4, name: "test5", quantity: 1 },
{ id: 5, name: "test6", quantity: 1 },
{ id: 5, name: "test7", quantity: 1 },
{ id: 6, name: "test8", quantity: 1 },
];
console.log(convert(arr));

Remove first matching item in a collection

Given a collection, how can I remove only the first item that matches a condition?
For example, given this collection:
[
{ id: 1, name: "don" },
{ id: 2, name: "don" },
{ id: 3, name: "james" },
{ id: 4, name: "james" }
]
Filter out the first result that matches { name: "james" }.
Result:
[
{ id: 1, name: "don" },
{ id: 2, name: "don" },
{ id: 4, name: "james" }
]
Using underscore.js _.without and _.findWhere
var myarray = [
{ id: 1, name: "don" },
{ id: 2, name: "don" },
{ id: 3, name: "james" },
{ id: 4, name: "james" }
];
var arr = _.without(myarray, _.findWhere(myarray, {
name: "james"
}));
console.log(arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Using Lodash _.without and _.find
var myarray = [
{ id: 1, name: "don" },
{ id: 2, name: "don" },
{ id: 3, name: "james" },
{ id: 4, name: "james" }
];
var result =_.without(myarray, _.find(myarray, { name: "james" }));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
Are you are looking a solution like this?
Iterate and update an array using Array.prototype.splice.
var arr = [
{ id: 1, name: "don" },
{ id: 2, name: "don" },
{ id: 3, name: "james" },
{ id: 4, name: "james" }
];
// loop and remove the first match from the above array
for (i = 0; i < arr.length; i++) {
if (arr[i].name == "james"){
arr.splice(i, 1);
break;
}
}
// write into the browser console
console.log(arr);
with Lodash
var newArray = _.without(array, _.find(array, { name: "james" }));

SQL-joining of two or more arrays in JavaScript

I have one-page web app with few arrays, that are logically linked: records from "users" refers to records in "user_types", "charges" refers to "users", etc:
var users = [
{ id: "u0001", name: "John", user_type_id: "1" },
{ id: "u0002", name: "Bob", user_type_id: "1" },
{ id: "u0003", name: "Alice", user_type_id: "5" },
{ id: "u0004", name: "Jennifer", user_type_id: "5" },
// ... more
];
var user_types = [
{ id: "1", name: "Regular Clients"},
{ id: "5", name: "VIP Clients"},
// ... more
];
var charges = [
{ id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", },
{ id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", },
{ id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", },
{ id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", },
{ id: "7469", user_id: null , date: "2016-01-01", amount: "3.99", },
// ... more
];
I need to display them in linked manner, similar to the product of following SQL:
SELECT
charges.date,
charges.amount,
users.name,
user_types.name
FROM
charges
LEFT OUTER JOIN users ON users.id = charges.user_id
LEFT OUTER JOIN user_types ON user_types.id = users.user_type_id
I know I can create API call with this SQL query on server, but I would like to avoid that because tables are already loaded in the web app.
What is the simplest way to join them in memory?
If small library is OK, this can be done with StrelkiJS:
var users = new StrelkiJS.IndexedArray();
users.loadArray([
{ id: "u0001", name: "John", user_type_id: "1" },
{ id: "u0002", name: "Bob", user_type_id: "1" },
{ id: "u0003", name: "Alice", user_type_id: "5" },
{ id: "u0004", name: "Jennifer", user_type_id: "5" },
// ... more
]);
var user_types = new StrelkiJS.IndexedArray();
user_types.loadArray([
{ id: "1", name: "Regular Clients"},
{ id: "5", name: "VIP Clients"},
// ... more
]);
var charges = new StrelkiJS.IndexedArray();
charges.loadArray([
{ id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", },
{ id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", },
{ id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", },
{ id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", },
{ id: "7469", user_id: null , date: "2016-01-01", amount: "3.99", },
// ... more
]);
var result = charges.query([{
from_col: "user_id",
to_table: users,
to_col: "id",
type: "outer",
join: [{
from_col: "user_type_id",
to_table: user_types,
to_col: "id",
type: "outer",
}]
}])
Result will be joined array of following structure:
[
[
{"id":"7443","user_id":"u0001","date":"2016-01-01","amount":"3.99"},
{"id":"u0001","name":"John","user_type_id":"1"},
{"id":"1","name":"Regular Clients"}
],
[
{"id":"7445","user_id":"u0001","date":"2016-01-01","amount":"4.02"},
{"id":"u0001","name":"John","user_type_id":"1"},
{"id":"1","name":"Regular Clients"}
],
[
{"id":"7448","user_id":"u0001","date":"2016-01-01","amount":"6.99"},
{"id":"u0001","name":"John","user_type_id":"1"},
{"id":"1","name":"Regular Clients"}
],
[
{"id":"7453","user_id":"u0003","date":"2016-01-01","amount":"3.00"},
{"id":"u0003","name":"Alice","user_type_id":"5"},
{"id":"5","name":"VIP Clients"}
],
[
{"id":"7469","user_id":null,"date":"2016-01-01","amount":"3.99"},
null,
null
]
]
If you can modify the way users and user_types are being populated then you can do this pretty quickly.
You would need to change users and user_types to objects so you have something like this:
// make users an object with the id as the key
var users = {
"u0001" : { name: "John", user_type_id: "1" },
"u0002" : { name: "Bob", user_type_id: "1" },
"u0003" : { name: "Alice", user_type_id: "5" },
"u0004" : { name: "Jennifer", user_type_id: "5" }
};
// same for user_types
var user_types = {
"1" : { name: "Regular Clients" },
"5" : { name: "VIP Clients" }
};
var charges = [
{ id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", },
{ id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", },
{ id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", },
{ id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", },
{ id: "7469", user_id: null , date: "2016-01-01", amount: "3.99", }
];
// now you can just loop through and use object key lookups:
var out = [];
for(var i = 0, numCharges = charges.length; i < numCharges; ++i)
{
var currentCharge = charges[i];
if(currentCharge.user_id === null) continue;
out.push([
currentCharge.date,
currentCharge.amount,
// get the current charges user_id and look up the name from users
users[currentCharge.user_id].name,
// same as above but use the user_type_id to get the user_type name
user_types[users[currentCharge.user_id].user_type_id].name
]);
}
console.log(out);
This proposal features IMTheNachoMan solution, with the extension of generation necessary objects from the given data.
It includes all rows of charges, because with SQL, the rows are returned too.
The problem with null values are here tested and null is then returned.
var users = [{ id: "u0001", name: "John", user_type_id: "1" }, { id: "u0002", name: "Bob", user_type_id: "1" }, { id: "u0003", name: "Alice", user_type_id: "5" }, { id: "u0004", name: "Jennifer", user_type_id: "5" }],
user_types = [{ id: "1", name: "Regular Clients" }, { id: "5", name: "VIP Clients" }],
charges = [{ id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", }, { id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", }, { id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", }, { id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", }, { id: "7469", user_id: null, date: "2016-01-01", amount: "3.99", }],
user = Object.create(null),
type = Object.create(null),
result;
users.forEach(function (u) {
user[u.id] = u;
});
user_types.forEach(function (t) {
type[t.id] = t;
});
result = charges.map(function (charge) {
return {
'charges.date': charge.date,
'charges.amount': charge.amount,
'users.name': charge.user_id === null ? null : user[charge.user_id].name,
'user_types': charge.user_id === null ? null : type[user[charge.user_id].user_type_id].name,
};
});
console.log(result);
Make users a map, so you can use users['u0001']. Then loop through charges and do users[current_charge.user_id].charges.push(current_charge). Each user in users should have a charges property initialized as an empty array. You can do that when you turn the users array into a id => user map.
You don't need anything special here, just two loops through users and charges:
var users_map = {};
var i;
for(i = 0; i < users.length; i++) {
users_map[users[i].id] = users[i];
users_map[users[i].id].charges = [];
}
for(i = 0; i < charges.length; i++) {
users_map[charge[i].user_id].charges.push(charge[i]);
}
If you really need the final "result" to be an array, not a map, you can loop through users_map again and turn it into an array.
A really simple solution leveraging modern JS stuff would be this:
var joined_data = Object.keys(users_map).map(function (key) {
return users_map[key];
});
You can make the above code a lot prettier with lodash or another similar library.
The only way to do this without restructuring the objects is to loop and filter.
You can optimise it slightly by processing the users and their types first, but that's about it...
var users = [
{ id: "u0001", name: "John", user_type_id: "1" },
{ id: "u0002", name: "Bob", user_type_id: "1" },
{ id: "u0003", name: "Alice", user_type_id: "5" },
{ id: "u0004", name: "Jennifer", user_type_id: "5" },
// ... more
];
var user_types = [
{ id: "1", name: "Regular Clients"},
{ id: "5", name: "VIP Clients"},
// ... more
];
var charges = [
{ id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", },
{ id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", },
{ id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", },
{ id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", },
{ id: "7469", user_id: null , date: "2016-01-01", amount: "3.99", },
// ... more
];
// pre-process users
var usersPlusTypes = users.map(function(u) {
var foundUserTypes = user_types.filter(function(ut) {
return ut.id == u.user_type_id;
});
return {
id: u.id,
user: u,
userType: foundUserTypes.length ? foundUserTypes[0] : null
}
})
// now link charges to users
var results = charges.map(function(c) {
var user = usersPlusTypes.filter(function(upt) {
return upt.id == c.user_id;
});
return {
date: c.date,
amount: c.amount,
userName: user.length ? user[0].user.name : null,
userTypeName: user.length && user[0].userType ? user[0].userType.name : null,
};
});
console.log(results);

Categories