I am working on an angular app. I have an array "*identify duplicate" which is something like this.
[
{
Name: "Jack",
Id: "1"
},
{
Name: "Rose",
Id: "2"
},
{
Name: "Jack",
Id: "4"
},
{
Name: "Jack",
Id: "4"
}
]
If I get same name and Id I want to remove both. Like in above example in last two indexes name and Id are same "jack" But I don't want to delete any data if name is name and id is different. Like in above example I want to keep Jack with Id "1" in my array but want to delete last two "Jack" with Id "4". How can I do that?
You can do this with several methods like filter,reduce,map etc... I create two example below.
First one is simple forEach method. In a loop filter if there is same Name element then push to new array
In second reduce method again filter element if there is same Name element then push to new array.
var arr=[{Name: "Jack",Id: "1"},{Name: "Rose",Id: "2"},{Name: "Jack", Id: "4"},{Name: "Jack", Id: "4"}];
//simple foreach method
var result=[];
arr.forEach(el=>{
if(result.filter(x=>x.Name==el.Name).length==0){result.push(el);}
});
console.log(result);
//reduce method
var result2=arr.reduce((r, { Name, Id }) => {
var temp = r.find(o => Name === o.Name);
if (!temp) { r.push(temp = { Name, Id });}
return r;
}, []);
console.log(result2);
Using Map()
var arr = [{Name:"Jack",Id:"1"},{Name:"Rose",Id:"2"},{Name:"Jack",Id:"4"},{Name:"Jack",Id:"4"}]
let res = [...arr.reduce((a, i) => a.has(i.Name) ? a : a.set(i.Name, i), new Map).values()]
console.log(res)
Try this:
var a = [
{
"Name": "Jack",
"Id": "1"
},
{
"Name": "Jack",
"Id": "4"
},
{
"Name": "Jack",
"Id": "4"
}
]
var jsonObject = a.map(JSON.stringify);
console.log(jsonObject);
var uniqueSet = new Set(jsonObject);
var uniqueArray = Array.from(uniqueSet).map(JSON.parse);
console.log(uniqueArray);
If you can use Javascript libraries such as underscore or lodash, I recommend having a look at _.uniq function in their libraries. From lodash
var arr=[ { Name: "Jack", Id: "1" }, { Name: "Rose", Id: "2" }, { Name: "Jack", Id: "4" }, { Name: "Jack", Id: "4" } ]; var updated_data = _.uniq(arr, 'Name');
Related
I have a response value which is dynamic which i need to store in redux state,
Response consist of array of object and and name
ex :
{data:[
{name:"abc",age:"10",id:"10"}
{name:"abc",age:"15",id:"20"}
{name:"def",age:"15",id:"20"}
]
name: "abc"
}
So if the name is same I need to create array with the name.
Expected :
abc:[
{name:"abc",age:"10",id:"10"}
{name:"abc",age:"15",id:"20"}
]
something I tried
data.map(function(o) {
if(data.name ==o.name)
return name[o];
});
If you're wanting a new object with a key of the name property you could try something like this
const response = {
data: [{
name: "abc",
age: "10",
id: "10"
},
{
name: "abc",
age: "15",
id: "20"
},
{
name: "def",
age: "15",
id: "20"
},
],
name: "abc"
}
const createSet = (someData) => {
let key = someData.name
let data = someData.data.filter(e => e.name === key)
return {
[key]: data
}
}
console.log(createSet(response))
You can extract duplicated using reduce and filter :
var data = {
data:[
{name:"abc",age:"10",id:"10"},
{name:"abc",age:"15",id:"20"},
{name:"def",age:"15",id:"20"}
],
name: "abc"
}
const lookup = data.data.reduce((a, e) => {
a[e.name] = ++a[e.name] || 0;
return a;
}, {});
console.log(data.data.filter(e => lookup[e.name]));
arr1 = [
{
"levelNumber": "2",
"name": "abc",
},
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
}
]
my result array should have objects with max levelNumber i.e 3 in this case.
it should look like:
resultArr = [
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
}
]
note that here levelNumber can be anything..
please help me with the generic nodejs code to get duplicate max value objects
You can first find the max level of all the objects in the array and then filter the array
arr1 = [
{
"levelNumber": "2",
"name": "abc",
},
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
}
]
const maxLevel = String(Math.max(...arr1.map(obj => Number(obj.levelNumber))))
const maxLevelObjects = arr1.filter(obj => obj.levelNumber === maxLevel)
console.log(maxLevelObjects);
const data = [
{
"levelNumber": "2",
"name": "abc",
},
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
}
];
const levelNumbers = data.map((item) => parseInt(item.levelNumber));
const maxLevelNumber = Math.max(...levelNumbers).toString();
const highestLevelItems = data.filter((item) => item.levelNumber == maxLevelNumber);
console.log(highestLevelItems);
/* output
[
{ levelNumber: '3', name: 'abc' },
{ levelNumber: '3', name: 'raks' }
]
*/
EDIT
As #nat mentioned in comment:
if I add one more object in the array, with name = 'raks & levelNumber = '4' then it should display maximum levelNumber wrt that particular name. i.e.
{ "levelNumber": "3", "name": "abc" }, { "levelNumber": "4", "name": "raks" }
To achieve this, you have to:
make a Set of names
make a separate empty array to hold final result
repeat the above process for each name and add result in the array
return complete result
const data = [
{
"levelNumber": "2",
"name": "abc",
},
{
"levelNumber": "3",
"name": "abc"
},
{
"levelNumber": "3",
"name": "raks",
},
{
"levelNumber": "4",
"name": "raks",
},
{
"levelNumber": "5",
"name": "raks",
}
];
// 1.
const names = new Set(data.map((item) => item.name)); // Set is used to get only unique items
// 2.
const result = []; // For normal JS
// const result: Array<{levelNumber: string, name: string}> = []; // For TS
// 3.
names.forEach((name) => {
/* minify data (filter items with only particular name) e.g. [{levelNumber: '2', name: 'abc'}, {levelNumber: '3', name: 'abc'}] */
const minifiedData = data.filter((item) => item.name === name);
/* same process, now for minified array */
const levelNumbers = minifiedData.map((item) => parseInt(item.levelNumber));
const maxLevelNumber = Math.max(...levelNumbers).toString();
minifiedData.forEach((item) => {
if (item.levelNumber == maxLevelNumber)
result.push(item); // push every matching item (item with highest level) in final result
});
});
// 4.
console.log(result);
const arr1 = [
{
levelNumber: '2',
name: 'abc',
},
{
levelNumber: '3',
name: 'abc',
},
{
levelNumber: '3',
name: 'raks',
},
];
const getHighLevelElements = (array) => {
if (array.length === 0) return null;
array.sort((elem1, elem2) => {
if (Number(elem1.levelNumber) < Number(elem2.levelNumber)) {
return 1;
}
if (Number(elem1.levelNumber) > Number(elem2.levelNumber)) {
return -1;
}
return 0;
});
return array.filter((elem) => elem.levelNumber === array[0].levelNumber);
};
const resultArr = getHighLevelElements([...arr1]);
console.log(resultArr);
I would first have a variable called highestLevel to store the highest level number found in the array of objects (will be used later while looping), loop through the whole array and checking every key levelNumber and storing that number IF highestLevel is lower than the value of the current object levelNumber.
After I've looped through the array and got the actual highestLevel number, I would loop through again and only get the objects that are equivalent to my variable highestLevel
You can just iterate one time over arr1 with Array.prototype.reduce()
Code:
const arr1 = [{levelNumber: '2',name: 'abc',},{levelNumber: '3',name: 'abc',},{levelNumber: '3',name: 'raks'}]
const result = arr1.reduce((a, c) => !a.length || +c.levelNumber === +a[0].levelNumber
? [...a, c]
: +c.levelNumber > +a[0].levelNumber
? [c]
: a,
[])
console.log(result)
Having an array of objects like this:
[{"event_id":1,"person":"John"},
{"event_id":2,"person":"John"},
{"event_id":3,"person":"Mike"},
{"event_id":1,"person":"Mike"},
{"event_id":1,"person":"Anna"},
{"event_id":3,"person":"Anna"}]
the wanted result should combine them based on the event_id and show them in a table structure like this:
1 John, Mike, Ana
2 John
3 Mike, Anna
Each row represents an event and the rows contains the people who participated in that event.
I don't know how do to this in JavaScript. Any suggestions?
You can use reduce:
const data = [
{ event_id: 1, person: 'John' },
{ event_id: 2, person: 'John' },
{ event_id: 3, person: 'Mike' },
{ event_id: 1, person: 'Mike' },
{ event_id: 1, person: 'Anna' },
{ event_id: 3, person: 'Anna' },
];
const result = data.reduce(
(acc, val) => ({
...acc,
[val.event_id]: acc[val.event_id] ? [...acc[val.event_id], val.person] : [val.person],
}),
{},
);
console.log(result);
You can use a Map with .reduce() to first group your objects, where the event_id is the key in the map, and the value is an array of accumulated person values for the same event_id values. You can then use Array.from() to map each [key, value] entry to a string of HTML for the table rows and data. You can then add this string to your HTML like so:
const arr = [{"event_id":1,"person":"John"},
{"event_id":2,"person":"John"},
{"event_id":3,"person":"Mike"},
{"event_id":1,"person":"Mike"},
{"event_id":1,"person":"Anna"},
{"event_id":3,"person":"Anna"}];
const table = `
<table>
${Array.from(
arr.reduce((m, {event_id:id, person}) => m.set(id, [...(m.get(id) || []), person]), new Map),
([key, arr]) => `<tr><td>${key}</td><td>${arr.join(', ')}</td></tr>`
).join('')}
</table>
`;
document.body.innerHTML = table;
I recommend you to use Map data type.
Map is a collection of keyed data items, just like an Object. But the
main difference is that Map allows keys of any type.
First of all we iterate on Array of Objects, then we check if there is any event_id in Map we push Object.person to the value of event_id entry :
const listOfObjects = [
{ "event_id": 1, "person": "John" },
{ "event_id": 2, "person": "John" },
{ "event_id": 3, "person": "Mike" },
{ "event_id": 1, "person": "Mike" },
{ "event_id": 1, "person": "Anna" },
{ "event_id": 3, "person": "Anna" }];
let eventIdCollection = new Map();
listOfObjects.forEach(obj => {
if (eventIdCollection.has(obj.event_id)) {
let persons = eventIdCollection.get(obj.event_id);
persons.push(obj.person);
eventIdCollection.set(obj.event_id, persons);
}
else {
eventIdCollection.set(obj.event_id, [obj.person]);
}
});
the result is Map of event_id to Array of persons.
let buckets = [
{ first: { fname: "David", locations: ["q1,""q2,"q3","q4"] } },
{ second: { fname: "Eric", locations: ["a1","a2","a3","a4"] } },
];
test : ["a1","q2","q4","w100"];
Here, the elements inside test can be and any element that is not present in locations needs to be ignored, since w100 is not present it needs to be ignored
For final output I needed something like below:
Since the first element of test belongs to location from second object, I need to have output as:
{
fname: "Eric",
testing: "a1",
name: "copying a1"
},
Since the second element of test belongs to location form first object, I need to have output as:
{
fname: "David",
testing: "q2",
name: "copying q2"
}
And the same rule for the third element too:
{
fname: "David",
testing: "q4",
name: "copying "q4"
}
const buckets = [{
first: {
fname: "David",
locations: ["q1", "q2", "q3", "q4"]
}
},
{
second: {
fname: "Eric",
locations: ["a1", "a2", "a3", "a4"]
}
},
]
const test = ["a1", "q2", "q4", "w100"];
//must take in objects you suspect test values derive from
function compare(obj1, obj2, test) {
//reduce each test ele into an initial empty array output
const output = test.reduce((arr, test) => {
//if test ele belongs to first obj
if (obj1.locations.indexOf(test) > -1) {
//add the following to output
arr.push({
fname: obj1.fname,
testing: test,
name: `copying ${test}`
})
//if test ele belongs to second obj
} else if (obj2.locations.indexOf(test) > -1) {
//add the following to output
arr.push({
fname: obj2.fname,
testing: test,
name: `copying ${test}`
})
}
//repeat cycle
return arr;
}, [])
return output;
}
//pass in the nested objects, not the root objects
console.log(compare(buckets[0].first, buckets[1].second, test));
Your question is not totally clear to me, but I'll give it a shot:
let buckets = [{
first: {
fname: "David",
locations: ["q1", "q2", "q3", "q4"]
}
}, {
second: {
fname: "Eric",
locations: ["a1", "a2", "a3", "a4"]
}
}, ];
const test = ["a1", "q2", "q4", "w100"];
// the "first" and "second" keys are not used for anything
const flatBuckets = buckets.map(e => {
return Object.values(e)[0]
})
// creating the result:
const result = test.map(e => {
const c = flatBuckets.find(el => {
return el.locations.includes(e)
})
return c ? {
fname: c.fname,
testing: e,
name: `copying ${ e }`
} : null
}).filter(e => e)
console.log(result)
I have a JSON file
{
"data": [
{
"name": "Jake",
"id": "123"
},
{
"name": "Bob",
"id": "234"
}]
}
with all id's unique, and say I have an array of banned ids ["123","423"] and I would like to delete all entries that have an id number in the array (so as an output I'd like the following).
{
"data": [
{
"name": "Bob",
"id": "234"
}]
}
What would be a moderately efficient way (runs in a few seconds on an ordinary computer) to achieve this if there's a few thousand entries in the JSON and array?
You can use the Array.prototype.filter() method in conjunction with .indexOf():
var bannedIds = ["123", "423"];
var input = {
"data": [
{
"name": "Jake",
"id": "123"
},
{
"name": "Bob",
"id": "234"
}]
};
input.data = input.data.filter(function(v) {
return bannedIds.indexOf(v.id) === -1;
});
console.log(input);
If you don't want to overwrite the original array then just assign the result of the .filter() call to a new variable.
If the above turns out to be too slow with your large amount of data, you can try replacing .filter() with a conventional for loop, and/or replacing .indexOf() with a lookup object created from the array of banned ids.
If you can use ES6, you can do this:
const source = {
"data": [
{
"name": "Jake",
"id": "123"
},
{
"name": "Bob",
"id": "234"
}
]
};
const banned = ["123", "423"];
// O(n) startup cost for constant access time later
const bannedSet = new Set(banned);
// O(n)
const result = source.data.filter(x => !bannedSet.has(x.id));
console.log(result);
As mentioned in the comments, there's a startup cost for creating the Set. However, this lets you then call Set.prototype.has, which is constant.
Then, it's just a matter of iterating over every element and filtering out the ones that are in the banned set.
If you can't use ES6, you could replace Set with a plain JS object. If you have to support IE<9, use a polyfill for Array.prototype.filter (thanks #nnnnnn).
UPDATE
#SpencerWieczorek points out that the ES6 spec seems to indicate that Set.prototype.has iterates. I spoke too soon about the lookup being constant (I was carrying over my experience from other languages). Typically, sets will do better than O(n), e.g. constant or O(log n) depending on the underlying implementation. Your mileage may vary, so nnnnnn's answer may be faster in some cases.
Try a few of the solutions here with large amounts of data to confirm.
EDIT
I shied away from using filter or the like because that involves creating a new array. That's actually probably fine for the data sizes we're talking about, but the approach I have below is more efficient.
On my laptop, this whole program runs in about 0.2 seconds. (It uses 10,000 entries and 100 banned IDs.)
var o = {
data: []
};
for (var i = 0; i < 10000; i++) {
o.data.push({
name: i % 2 === 0 ? 'Jake' : 'Bob', // couldn't think of more names :-)
id: ''+i // convert to string
});
}
var banned = {};
for (var i = 0; i < 100; i++) {
banned[''+(i * 3)] = true; // ban 0, 3, 6, 9, 12, ...
}
for (var i = o.data.length - 1; i >= 0; i--) {
if (banned[o.data[i].id]) {
o.data.splice(i, 1);
}
}
console.log(o);
// { data:
// [ { name: 'Bob', id: '1' },
// { name: 'Jake', id: '2' },
// { name: 'Jake', id: '4' },
// { name: 'Bob', id: '5' },
// { name: 'Bob', id: '7' },
// { name: 'Jake', id: '8' },
// { name: 'Jake', id: '10' },
// ...
I am assuming that you have already parsed the JSON data and you have a variable pointing to the array you want to filter. Also, you have an array with the "banned" IDs.
var data = [{
"name": "Jake",
"id": "123"
}, {
"name": "Bob",
"id": "234"
}, {
"name": "Joe",
"id": "345"
}];
var banned = ["123", "345"];
The following function wil probably do the best job that can be done in terms of performance:
// Modifies the data array "in place", removing all elements
// whose IDs are found in the "banned" array
function removeBanned(data, banned) {
// Index the "banned" IDs by writing them as the properties
// of a JS object for really quick read access later on
var bannedObj = {};
banned.forEach(function(b) { bannedObj[b] = true; });
var index = data.length - 1;
while (index >= 0) {
if (bannedObj[data[index].id]) {
data.splice(index, 1);
}
--index;
}
}
This one seems fast enough, but I'd suggest you make a free clean copy instead of modifying the existing array, - it may be faster.
function filterout(o,p,f) {
var i = 0; f = f.join();
while( o[i] ) {
if( f.match( o[i][p] ) ){ o.splice(i,1) }
i++
};
}
var filter = ["123","423"];
var object =
{
"data": [
{
"name": "John",
"id": "723"
},
{
"name": "Jake",
"id": "123"
},
{
"name": "Bob",
"id": "234"
}]
};
filterout( object.data, "id", filter );
console.log(JSON.stringify( object ));