I have an array of objects
let myArray = [
{
id: 'first',
name: 'john',
},
{
id: 'second',
name: 'Emmy',
},
{
id: 'third',
name: 'Lazarus',
}
]
and an array
let sorter = ['second', 'third', 'first']
I would like to use lodash sorting method to sort my objects according to their position in sorter.
So that the output would be
let mySortedArray = [
{
id: 'second',
name: 'Emmy',
},
{
id: 'third',
name: 'Lazarus',
},
{
id: 'first',
name: 'john',
}
]
Is it possible to do so?
You can achieve this by using map and find:
let myArray = [
{
id: "first",
name: "john"
},
{
id: "second",
name: "Emmy"
},
{
id: "third",
name: "Lazarus"
}
];
let sorter = ["second", "third", "first"];
let mySortedArray = sorter.map(x => myArray.find(y => y.id === x));
console.log(mySortedArray);
Using lodash you can use _.sortBy
let myArray = [
{
id: 'first',
name: 'john',
},
{
id: 'second',
name: 'Emmy',
},
{
id: 'third',
name: 'Lazarus',
}
]
let sorter = ['second', 'third', 'first']
console.log(_.sortBy(myArray,(i) => {return sorter.indexOf(i.id)}))
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
If you want to sort the array in-place, you don't need Lodash, you can easily do it with vanilla JavaScript
let myArray = [
{
id: 'first',
name: 'john',
},
{
id: 'second',
name: 'Emmy',
},
{
id: 'third',
name: 'Lazarus',
}
]
let sorter = ['second', 'third', 'first']
//create a lookup table (map) to save looking through the array
const sortLookup = new Map();
//populate with element as key - index as value
sorter.forEach((id, index) => sortLookup.set(id, index));
//sort using the indexes of sorter
myArray.sort((a, b) => sortLookup.get(a.id) - sortLookup.get(b.id))
console.log(myArray)
This is using a Map but the same can easily be accomplished with a plain JavaScript Object {}. You don't even need to pre-compute the lookup myArray.sort((a, b) => sorter.indexOf(a.id) - sorter.indexOf(b.id)) would give the exact same output but it would mean that instead of traversing sorter once for a complexity of O(n), you potentially have O(n^m)or O(n^n) (if both arrays are the same length)
Since you have an index array in the case of the sorter you can _.keyBy the main array and then use the sorter to access by index:
let myArray = [ { id: 'first', name: 'john', }, { id: 'second', name: 'Emmy', }, { id: 'third', name: 'Lazarus', } ]
let sorter = ['second', 'third', 'first']
const idMap = _.keyBy(myArray, 'id')
const result = _.map(sorter, x => idMap[x])
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
This should perform better since you only do the idMap once and then access it by index.
Related
in an arry of objects i want to remove object which have same id (duplicated data) using javascript.
below is the input array
const input = [
{
id: '1',
name: 'first',
},
{
id: '1',
name: 'first',
},
{
id: '2',
name: 'second',
},
{
id: '2',
name: 'second',
},
]
so as you see from above array there are duplicating data with id '1' and '2'.
if there is similar id i want include only one
so the expected output is like below,
const output = [
{
id: '1',
name: 'first',
},
{
id: '2',
name: 'second',
},
]
how can i do this. could someone help me with this. i am new to programming thanks.
You can use reduce to filter data from the array based on some condition like bellow
const input = [
{
id: '1',
name: 'first',
},
{
id: '1',
name: 'first',
},
{
id: '2',
name: 'second',
},
{
id: '2',
name: 'second',
},
]
const result = input.reduce((accumulator, current) => {
let exists = accumulator.find(item => {
return item.id === current.id;
});
if(!exists) {
accumulator = accumulator.concat(current);
}
return accumulator;
}, []);
console.log(result);
Similar to this answer. You will have to change the const to let while declaring input though, or use a new variable I suppose.
filtered_input = input.filter((value, index, self) =>
index === self.findIndex((t) => (
t.id === value.id
))
)
There is a lot of good approachs here.
Here is my approach for removing matching property from the original array and sending it back in the return if found.
I prefer to use this one, if you are looping through a external array and matching them, this way you dont need to loop through the whole array again and again for each, because while you are finding the matches it keeps removing them from the original array, increasing performance.
Note that this will return the first match found
let id = "601985b485d9281d64056953"
let contacts = [{
...,
parent: "601985b485d9281d64056953",
...,
},
{
...,
parent: "601985b485d9281d64065128",
...,
}
]
function findAndRemoveObjectFromArray(array, internalProperty, externalProperty, convertType = "string", returnObject = false) {
let objIndex = -1
if (convertType === "string") objIndex = array.findIndex((obj) => String(obj[`${internalProperty}`]) === String(externalProperty));
if (convertType === "number") objIndex = array.findIndex((obj) => Number(obj[`${internalProperty}`]) === Number(externalProperty));
if (objIndex > -1) {
const object = array.splice(objIndex, 1);
if (returnObject) return object.shift()
return object
}
return [];
}
let currentContact = findAndRemoveObjectFromArray(contacts, "parent", id, 'string', true)
// Results:{..., parent: "601985b485d9281d64056953",...}
you could use Set to get rid of the duplicate data like this
const input = [
{
id: '1',
name: 'first',
},
{
id: '1',
name: 'first',
},
{
id: '2',
name: 'second',
},
{
id: '2',
name: 'second',
},
]
const result = [...new Set(input.map(JSON.stringify))].map(JSON.parse)
console.log(result)
Below is another approach
const input = [
{
id: '1',
name: 'first',
},
{
id: '1',
name: 'first',
},
{
id: '2',
name: 'second',
},
{
id: '2',
name: 'second',
},
];
const uniqueIds = new Set();
const uniqueList = input.filter(element => {
const isDuplicate = uniqueIds.has(element.id);
uniqueIds.add(element.id);
return !isDuplicate;
});
console.log(uniqueList);
I am trying to return the objects which has key of submittedDate. But if i am trying with find() It's returning only the first object. And for map it's returning undefined for object not having submittedDate key. Please find my code with data and also the result I want. thanks in advance.
const data = [
{
id: '1',
name: 'Tully Stark',
submittedData:'mmmmm'
},
{
id:'2',
name: 'Nalani Romanova',
},
{
id:'3',
name: 'Nalani Romanova',
submittedData:'mmmmm'
}
]
const submitDate = data.find(item => item.submittedData)
console.log(submitDate)
data to return
const returnData = [
{
id: '1',
name: 'Tully Stark',
submittedData:'mmmmm'
},
{
id:'3',
name: 'Nalani Romanova',
submittedData:'mmmmm'
}
]
the .find by definition only returns the first matching object.
Array.prototype.find()
The find() method returns the value of the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
You need to use .filter
const submitDate = data.filter(item => item.submittedData)
const data = [{
id: '1',
name: 'Tully Stark',
submittedData: 'mmmmm'
},
{
id: '2',
name: 'Nalani Romanova',
},
{
id: '3',
name: 'Nalani Romanova',
submittedData: 'mmmmm'
}
]
const submitDate = data.filter(item => item.submittedData)
console.log(submitDate)
You can use Array.filter(), this will return all matching items.
const data = [ { id: '1', name: 'Tully Stark', submittedData:'mmmmm' }, { id:'2', name: 'Nalani Romanova', }, { id:'3', name: 'Nalani Romanova', submittedData:'mmmmm' } ]
const submitDate = data.filter(item => item.submittedData)
console.log(submitDate)
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array like this :
const arr = [{name: "some", category: "Cat1"}, {name: "else", category: "Cat2"}, {name: "stuff", category: "Cat2"}]
I want to create a new array that only have category without pairs like that :
const newArr =[Cat1, Cat2]
and then map over it to render into my JSX Element. I try a lot of stuff but nothing worked Thanks for your help.
Using set object approach is the best approach. As it is already mentioned here I am going to show the other approaches.
Using Object:
You could use Array.prototype.forEach() method with Nullish coalescing operator (??) to do your job.
const arr = [
{ name: 'some', category: 'Cat1' },
{ name: 'else', category: 'Cat2' },
{ name: 'stuff', category: 'Cat2' },
];
const ret = {};
arr.forEach((x) => {
ret[x.category] = ret[x.category] ?? x.category;
});
console.log(Object.values(ret));
Using Map Object:
Map object stores key value pairs with the original insertion order. Although object is similar to map object but map object performs better when you need to do a lot of additions and deletions.
const arr = [
{ name: 'some', category: 'Cat1' },
{ name: 'else', category: 'Cat2' },
{ name: 'stuff', category: 'Cat2' },
];
const map = new Map();
const ret = [];
arr.forEach((x) => {
if (!map.has(x.category)) {
map.set(x.category, true);
ret.push(x.category);
}
});
console.log(ret);
Using Array:
Array.prototype.includes() method checks if the array contains the specified element or not and returns true or false based on that.
const arr = [
{ name: 'some', category: 'Cat1' },
{ name: 'else', category: 'Cat2' },
{ name: 'stuff', category: 'Cat2' },
];
const ret = [];
arr.forEach((x) => {
if (!ret.includes(x.category)) ret.push(x.category);
});
console.log(ret);
Using Reduce Method:
Array.prototype.reduce() method runs the given function for each iteration and reduces the array to a single value.
const arr = [
{ name: 'some', category: 'Cat1' },
{ name: 'else', category: 'Cat2' },
{ name: 'stuff', category: 'Cat2' },
];
const ret = Object.values(
arr.reduce((prev, c) => {
const p = prev;
const key = c.category;
if (!p[key]) p[key] = c.category;
return p;
}, {})
);
console.log(ret);
Try this
const arr = [
{name: "some", category: "Cat1"},
{name: "else", category: "Cat2"},
{name: "stuff", category: "Cat2"}
]
const result = arr.map(a=> a.category);
console.log([...new Set(result)]);
You are looking for something like this:
const arr = [{name: "some", category: "Cat1"}, {name: "else", category: "Cat2"}, {name: "stuff", category: "Cat2"}];
console.log([...new Set(arr.map(x => x.category))]);
I have 2 arrays:
blockedNumbers: ['123', '456', '789', '247'];
contacts: [
{name: 'Foo', numbers: [{ home:'123' }, { mobile:'456' }]},
{name: 'Bar', numbers: [{ home:'789' }]}
]
I want to create a new array of blocked contacts which will contain:
[
{ name: Foo, numbers: [{ home:'123' }, { mobile:'456' }] },
{name: 'Bar', numbers: [{ home:'789' }]}
'247'
]
So the solution I have tried first loops over blocked numbers, then forEach contact, if blocked number in numbers, push to array. But the result turns out as
[
'123'
{ name: Foo, numbers: ['123', '456'] },
{name: 'Bar', numbers: ['789']}
'456'
'789'
'247'
]
Code below:
const newBlacklistWithContacts = [];
blockedNumbers.forEach((blockedNumber) => {
contacts.map((contact) => {
// if blocked number in contacts
Object.keys(contact.numbers).forEach((e) => {
if (contact.numbers[e] === blockedNumber) {
const alreadyAdded = newBlacklistWithContacts.find(blacklistContact => blacklistContact.name === contact.name);
if (!alreadyAdded) {
return newBlacklistWithContacts.push({ name: contact.name, numbers: contact.numbers });
}
}
else if (!newBlacklistWithContacts.includes(blockedNumber)) {
return newBlacklistWithContacts.push(blockedNumber);
}
});
});
});
I'm sure there is a more efficient way to do this & actually return what I need? (All of the blacklisted contacts and if not in contacts, only the number) I am using js and React.js in this project
If your data set is really large, you can optimise your algorithm by doing O(1) lookups in a Set instead of using indexOf or includes which do O(n) lookups:
// Input
const blockedNumbers = ['123', '456', '789', '247'];
const contacts = [{name: 'Foo', numbers: [{ home:'123' }, { mobile:'456' }]}, {name: 'Bar', numbers: [{ home:'789' }]}];
// Algorithm
const set = new Set(blockedNumbers);
const notused = new Set(blockedNumbers);
const newBlacklistWithContacts = contacts.filter(contact =>
contact.numbers.map(obj => Object.values(obj)[0])
.filter(number => set.has(number) && (notused.delete(number) || true)).length
).concat(...notused);
// Output
console.log(newBlacklistWithContacts);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var blockedNumbers = ['123', '456', '789', '247'];
var contacts = [{
name: 'Foo',
numbers: [{
home: '123'
}, {
mobile: '456'
}]
},
{
name: 'Bar',
numbers: [{
home: '789'
}]
}
]
var blocks = [];
contacts.forEach(v1 => (v1.numbers.forEach(v2 => {
blocks.push(Object.values(v2)[0]);
})));
blockedNumbers.forEach(val => (blocks.indexOf(val) == -1 ? contacts.push(val) : null));
console.log(contacts);
I want to group and count the array below by id and name
var arr = [
{
id: 'A1',
name: 'andersson'
},
{
id: 'A1',
name: 'johansson'
},
{
id: 'A1',
name: 'andersson'
},
];
Expected output:
var aggregated = [
{
id: 'A1',
name: 'andersson',
count: 2
},
{
id: 'A1',
name: 'johansson',
count: 1
}
];
This should be a basic task but I cant find any handy way to do it with underscore. The _.groupBy function should probably be used somehow.
My dataset is big so performance is important.
You could first groupBy id and count and then use map to count each value.
var arr = [{
id: 'A1',
name: 'andersson'
}, {
id: 'A1',
name: 'johansson'
}, {
id: 'A1',
name: 'andersson'
}];
var result = _.chain(arr)
.groupBy(e => e.id + '|' + e.name)
.map(e => (e[0].count = e.length, e[0]))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>