Suppose I have a Javascript array like below.
const data = [
{ group: 'A', name: 'SD', testid:1},
{ group: 'B', name: 'FI',testid:2 },
{ group: 'A', name: 'MM', testid:1 },
{ group: 'B', name: 'CO', testid:2 },
{ group: 'A', name: 'QW', testid:1 }
];
I want to get two specific properties(group and testid).
I would like to retrieve unique values for those properties in my end result.
So my end result will be
{group:A,testid:1},{group:B,testid:2}
What I have tried so far is below.
data.map(item=>item.group).
But this will give me only one property and without any distinct values
How can I achieve this using latest ecmascript syntax in Javascript
you can reduce the array and check every time if the pair exists:
data.reduce((prev, el) =>{
if(prev.some(o => o.group == el.group && o.testid == el.testid))
return prev;
return [...prev, {group:el.group, testid:el.testid}]
}, [])
const data = [
{ group: 'A', name: 'SD', testid:1},
{ group: 'B', name: 'FI',testid:2 },
{ group: 'A', name: 'MM', testid:1 },
{ group: 'B', name: 'CO', testid:2 },
{ group: 'A', name: 'QW', testid:1 }
];
let result = data.reduce((prev, el) =>{
if(prev.some(o => o.group == el.group && o.testid == el.testid))
return prev;
return [...prev, {group:el.group, testid:el.testid}]
}, []);
console.log(result);
You can loop over it and get the desired result.
result = []
data.forEach(x=>{
if(!result.some(y=>y.group===x.group && x.testid===y.testid)){
result.push({group:x.group,testid:x.testid});
}
});
Use forEach loop and build an object with keys as uniq_id.
After the traverse, return Object.values of the above object.
const convert = (arr) => {
const res = {};
arr.forEach(({group, testid}) => {
// change uniq_id based on requirement
const uniq_id = `${group}-${testid}`;
res[uniq_id] = { group, testid};
});
return Object.values(res);
}
const data = [
{ group: 'A', name: 'SD', testid:1},
{ group: 'B', name: 'FI',testid:2 },
{ group: 'A', name: 'MM', testid:1 },
{ group: 'B', name: 'CO', testid:2 },
{ group: 'A', name: 'QW', testid:1 }
];
console.log(convert(data));
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 have the following array and object I would like to 'match'
const items = [
{ key: 1, name: 'A', owner: 'Alex', },
{ key: 2, name: 'B', owner: 'Barb', },
{ key: 3, name: 'C', owner: 'John', },
{ key: 4, name: 'D', owner: 'Barb', },
{ key: 5, name: 'E', owner: 'Alex', },
];
const owners = {
'Alex': { 1: [], 5: [] },
'John': { 3: [], },
'Barb': { 2: [], 4: [] },
}
I would like to have the following end result:
const ownersWithName = {
'Alex': [{ key: 1, name: 'A', }, { key: 5, name: 'E' }],
'Barb': [{ key: 2, name: 'B', }, { key: 4, name: 'D' }],
'John': [{ key: 3, name: 'C', }, ],
}
So far my solution is this:
function matchOwners (items, owners) {
const ownersWithName = {};
for (const item of items) {
if (owners[item.owner]) {
if (ownersWithName[item.owner]) {
ownersWithName[item.owner] = [ ...ownersWithName[item.owner], item];
} else {
ownersWithName[item.owner] = [item];
}
}
}
return ownersWithName;
}
This solution works, but i feel it's too verbose. i tried to use the spread operator without the if condition, but this needs the array to exist already, otherwise i get the error ownersWithName[item.owner] is not iterable. Is there a better way to do this?
Something like (completely untested):
ownersWithName = items.reduce((result, item) => {
if (owners[item.owner]) {
if (!(item.owner in result)) {
result[item.owner] = [];
}
result[item.owner].push({key: item.key, name: item.name});
}
return result;
}, {})
You can also simply achieve this by using Array.forEach() along with the Object.keys() method.
Live Demo (Descriptive comments has been added in the below code snippet) :
// Input array
const items = [
{ key: 1, name: 'A', owner: 'Alex', },
{ key: 2, name: 'B', owner: 'Barb', },
{ key: 3, name: 'C', owner: 'John', },
{ key: 4, name: 'D', owner: 'Barb', },
{ key: 5, name: 'E', owner: 'Alex', },
];
// Input object which should be used to match.
const owners = {
'Alex': { 1: [], 5: [] },
'John': { 3: [], },
'Barb': { 2: [], 4: [] },
};
// Declare an object which will store the final result.
const resultObj = {};
// Iterating over an items array to manipulate the data and make the final result set.
items.forEach(obj => {
resultObj[obj.owner] = !resultObj[obj.owner] ? [] : resultObj[obj.owner];
Object.keys(owners[obj.owner]).forEach(key => {
if (key == obj.key) {
resultObj[obj.owner].push({
key: obj.key,
name: obj.name
});
}
});
});
// Final output
console.log(resultObj);
I'm sort an array based on the keys in another array. If they find a match, it would move those items to the front of the array. But I can't think of a clean way to do this.
let myArray = [
{ id: 'a', name: 'Mal' },
{ id: 'b', name: 'Wash'},
{ id: 'c', name: 'Inara'},
{ id: 'd', name: 'Jayne'},
]
let sortArray = [
{ id: 'b' },
{ id: 'c' },
{ id: 'x' },
]
/* Expected result
myArray = [
{ id: 'b', name: 'Wash'},
{ id: 'c', name: 'Inara'},
{ id: 'a', name: 'Mal' },
{ id: 'd', name: 'Jayne'},
]
/*
Does anyone know a way to do this without just looping through it a bunch of times? Thanks
You could create a Map which maps each id in sortArray to its index. Use this priority map object to sort the first array.
const array = [{ id: 'a', name: 'Mal' }, { id: 'b', name: 'Wash'}, { id: 'c', name: 'Inara'}, { id: 'd', name: 'Jayne'}],
sortArray = [{ id: 'b' }, { id: 'c' }, { id: 'x' }],
map = new Map( sortArray.map((o, i) => [o.id, i]) )
array.sort((a,b) =>
( map.has(b.id) - map.has(a.id) ) || ( map.get(a.id) - map.get(b.id) )
)
console.log(array)
You could take an object ffor the wanted order of the items and a default value for unknown items.
let array = [{ id: 'a', name: 'Mal' }, { id: 'b', name: 'Wash'}, { id: 'c', name: 'Inara'}, { id: 'd', name: 'Jayne'}],
sortArray = [{ id: 'b' }, { id: 'c' }, { id: 'x' }],
order = Object.fromEntries(sortArray.map(({ id }, i) => [id, i + 1]));
array.sort(({ id: a }, { id: b }) =>
(order[a] || Number.MAX_VALUE) - (order[b] || Number.MAX_VALUE)
);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Say I have the following object:
const pages = [
{
name: 'a',
id: '1',
pages: [
{
name: 'b',
id: '1.1',
pages: []
},
{
name: 'c',
id: '1.2',
pages: [
{
name: 'd',
id: '1.2.1',
pages: []
}
]
},
]
},
{
name: 'e',
id: '2',
pages: []
}
]
I'd like to perform a function on this nested object that will return the 'path' to the object I'm searching for.
So something like
getPath(pages, '1.2.1')
will return:
[
{
name: 'a',
id: '1'
},
{
name: 'c',
id: '1.2'
},
{
name: 'd'
id: '1.2.1'
}
]
Here's what I have so far. Its just a recursive function to find the object I want. I'm stuck on how to build the path as I'm traversing through the object though.
const pages = [
{
name: 'a',
id: '1',
pages: [
{
name: 'b',
id: '1.1',
pages: []
},
{
name: 'c',
id: '1.2',
pages: [
{
name: 'd',
id: '1.2.1',
pages: []
}
]
},
]
},
{
name: 'e',
id: '2',
pages: []
}
]
function getPath(pages, pageId) {
let path = [];
for (let i = 0; i < pages.length; i++) {
const item = search(pages[i], pageId);
// build path here?
if (item) {
return item;
}
}
}
function search(obj, id) {
if (obj.id === id) {
return obj;
}
for (let i = 0; i < obj.pages.length; i++) {
const possibleResult = search(obj.pages[i], id);
if (possibleResult) {
return possibleResult;
}
}
}
console.log(getPath(pages, '1.2.1'))
You can use this alternative for getting the path, it's a recursive approach and uses an array called path as param in order to track the visited levels.
Assuming the ID's are uniques regardless of the location/level.
const pages = [ { name: 'a', id: '1', pages: [ { name: 'b', id: '1.1', pages: [] }, { name: 'c', id: '1.2', pages: [ { name: 'd', id: '1.2.1', pages: [] } ] }, ] }, { name: 'e', id: '2', pages: [] }];
const loop = (arr, target, index, path) => {
if (arr[index].id === target) {
path.push({name: arr[index].name, id: arr[index].id});
} else if (arr[index].pages.length) {
path.push({name: arr[index].name, id: arr[index].id});
arr[index].pages.forEach((_, i, a) => {
loop(a, target, i, path);
});
if (path[path.length - 1].id === arr[index].id) path.pop();
}
};
let getPath = (arr, target) => {
let path = [];
arr.forEach((_, i, a) => loop(a, target, i, path));
return path;
};
console.log(getPath(pages, '1.2.1'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an object like the one below and am looking for a way to add one property to each sub object in the tree. That is, I want to add a number to the object that counts up from 0 to n. I know I can traverse through the object with a recursive function but because of variable scope I'm not able to use a simple incremented variable to count up as I go through the tree.
Current Object:
var tree = [
{
name: 'a',
children: []
},{
name: 'b',
children: [
{
name: 'c',
children: []
}
]
},
{
name: 'd',
children: [
{
name: 'e',
children: [
{
name: 'f',
children: []
},
{
name: 'g',
children: []
}
]
}
]
}
];
Desired Object:
var tree = [
{
name: 'a',
number: 0,
children: []
},{
name: 'b',
number: 1,
children: [
{
name: 'c',
number: 2,
children: []
}
]
},
{
name: 'd',
number: 3,
children: [
{
name: 'e',
number: 4,
children: [
{
name: 'f',
number: 5,
children: []
},
{
name: 'g',
number: 6,
children: []
}
]
}
]
}
];
You just need to set counter variable outside of recursive function and increment it before you loop object.
var tree = [{"name":"a","children":[]},{"name":"b","children":[{"name":"c","children":[]}]},{"name":"d","children":[{"name":"e","children":[{"name":"f","children":[]},{"name":"g","children":[]}]}]}];
function addNumber(input) {
var counter = 0;
function rec(data) {
data.forEach(function(e) {
if (typeof e == 'object' && !Array.isArray(e)) {
e.number = counter++;
for (var p in e) {
if (typeof e[p] == 'object') rec(e[p])
}
}
})
}
rec(input)
}
addNumber(tree);
console.log(tree)
The problem you raise with the counter scope can be solved by defining the counter in a closure, within which you also have your recursive function.
Here is an ES6 function that does that:
function numberNodes(tree, n = 0) {
return (function recurse(children) {
return children.map( node => Object.assign({}, node, {
number: n++,
children: recurse(node.children)
}) );
})(tree);
}
// Sample data
var tree = [{ name: 'a', children: []},
{ name: 'b', children:
[{ name: 'c', children: []}]},
{ name: 'd', children:
[{ name: 'e', children:
[{ name: 'f', children: []}, { name: 'g', children: []}]}]}];
// Return tree with numbers added:
tree = numberNodes(tree);
// Output result
console.log(tree);
Note that this function does not mutate the tree you pass it, only the return value has the added properties. So this is a functional programming way of doing it.