Rename value if it is duplicate in json - - javascript

When there are multiple entries, I only want to append "_1","_2"....etc to json object, making sure no data is lost. If this is input:
names_array = [
{name: "a", age: 15},
{name: "a", age: 16},
{name: "a", age: 17},
{name: "b", age: 18}
{name: "b", age: 19}];
I want the output to be
names_array_new = [
{name: "a", age: 15},
{name: "a_1", age: 16},
{name: "a_2", age: 17},
{name: "b", age: 18}
{name: "b_1", age: 19}
];
I have searched for this but only found how to remove duplicates when entire objects are same or retain the last found value.

there are possibly better solutions, but i came up with such quick snippet. take it and make better.
name_changer = (n) => {
splitted = n.split("_");
if(splitted.length > 1) {
name = splitted[0];
ord = parseInt(splitted[1]);
return `${name}_${ord+1}`
} return `${n}_${1}`
}
stack = []
names_array = [
{name: "a", age: 15},
{name: "a", age: 16},
{name: "a", age: 17},
{name: "b", age: 18},
{name: "b", age: 19}
];
names_array.forEach(obj => {
if (stack.indexOf(obj.name) > -1) {
obj.name = name_changer(obj.name);
} stack.push(obj.name);
})
console.log(names_array);

You can use the function Array.prototype.reduce to generate the desired output.
The handler basically keeps the previous processed values in order to save the current count and finally we get the result from the accumulator.
let arr = [ {name: "a", age: 15}, {name: "a", age: 16}, {name: "a", age: 17}, {name: "b", age: 18}, {name: "b", age: 19}],
{result} = arr.reduce((a, {name: n, age}) => {
let key = a.counts[n] = (a.counts[n] || a.counts[n] === "" ? (+a.counts[n] + 1) : ""),
name = `${n}${key === "" ? "" : "_"}${key}`;
a.result.push({name, age});
return a;
}, {counts: {}, result: []});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can simply check the index and name of each a and b and then rename the value of that object keys with count++ with increment of count on each loop.
Just FYI - This can be improved more so wait for other solutions to come as well and choose what suits you best but this is simple and straight to your desired output!
Expected Output Demo:
var names_array = [{
name: "a",
age: 15
},
{
name: "a",
age: 16
},
{
name: "a",
age: 17
},
{
name: "b",
age: 18
}, {
name: "b",
age: 19
}
];
var countA = 1 //count increase for a
var countB = 1 //count increase for b
names_array.forEach(function(obj, index) {
if (obj.name == 'a' && index >= 1) {
obj.name = obj.name + '_' + countA++; //rename a
}
if (obj.name == 'b' && index >= 4) {
obj.name = obj.name + '_' + countB++; //rename b
}
});
console.log(names_array)

You could use a lookup object with key-value pairs of name value and its occurence
const nameValueLookup = {}
const namesArray = [
{ name: "a", age: 15 },
{ name: "a", age: 16 },
{ name: "a", age: 17 },
{ name: "b", age: 18 },
{ name: "b", age: 19 },
]
const res = namesArray.map((obj) => {
if (nameValueLookup[obj.name]) {
return { ...obj, name: `${obj.name}_${nameValueLookup[obj.name]++}` }
} else {
nameValueLookup[obj.name] = 1
return obj
}
})
console.log(res)

Related

Map + filter - Showing people that have < 18 years and abbreviate names

I wanted to be able to transform the age into a single array, so I would already know how to filter, then apply the mapping to only show people over 18 years old, in addition, to present the abbreviated names of the people.
Example:
name: "Raul", age: 27,
name: "Jose", age: 14,
name: "Maria", age: 52,
name: "Jesus", age: 17,
name: "Neo", age: 2
[Ra, Ma] -> Those are above 18, and their names go abbreviated
Here what i tried to do:
const amantesGatos = {
name: "Raul", age: 27,
name: "Jose", age: 14,
name: "Maria", age: 52,
name: "Jesus", age: 17,
name: "Neo", age: 2
};
// this needs to be filtered
const idade = amantesGatos.age
//i tried to destructuring this array
const nomeAbrev = [amantesGatos.map(n=>n[0] + n[1])]
//Tried to abbreviated names
//Filter above 18
const array = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 444, 17];
function above18(num) {
for (let i = 18; num < i; i--) {
if (num < i) {
return false;
}
}
return num;
}
console.log(array.filter(above18));
If I'm understanding correctly the desired outcome is [Ra, Ma]?
If so you can .filter.map.
const peeps = [
{name: "Raul", age: 27,},
{name: "Jose", age: 14,},
{name: "Maria", age: 52,},
{name: "Jesus", age: 17,},
{name: "Neo", age: 2}
]
const ofAgePeeps = peeps.filter(({age}) => age > 18)
const shortNames = ofAgePeeps.map(({name}) => name.substring(0,2))
You can also chain these...
peeps.filter(({age}) => age > 18).map(({name}) => name.substring(0,2))
That said your amantesGatos is an object with a bunch of duplicate keys and not an array. Which means it's really an object with only the last name and age. For example...
const obj = {
name: 'Tom', age: 2, name: 'Bob', age: 100
}
console.log(obj) // {name: 'Bob', age: 100}
You can do as following
let users = [
{ name: "Raul", age: 27,},
{name: "Jose", age: 14,},
{name: "Maria", age: 52,},
{name: "Jesus", age: 17,},
{name: "Neo", age: 2},
]
let data = users.filter(user => user.age > 18).map(user => user.name.slice(0, 2))
data would be [ 'Ra', 'Ma' ]

JS - Sorting array of objects

I'm trying to sort an array of objects in JS, but for some reason it doesnt work :(
This is the code I'm running:
let myArr = [{"name": "a", "age": 21}, {"name": "b", "age": 18}, {"name": "c", "age": 20}];
console.log(myArr.sort((a, b) => {
return a["age"] > b["age"];
}))
The output is the same array as it was declared:
[
{ name: 'a', age: 21 },
{ name: 'b', age: 18 },
{ name: 'c', age: 21 }
]
When I looked it up it seemed like it's written as it should, not really sure what went wrong.
Any idea what I did wrong?
Use - instead of >. It should return a Number type rather than Boolean type. see sort on mdn
let myArr = [
{ name: "a", age: 21 },
{ name: "b", age: 18 },
{ name: "c", age: 20 },
];
console.log(
myArr.sort((a, b) => {
return a["age"] - b["age"];
})
);
Using one-liner syntax using arrow function
let myArr = [
{ name: "a", age: 21 },
{ name: "b", age: 18 },
{ name: "c", age: 20 },
];
console.log(myArr.sort((a, b) => a.age - b.age));

how to transform an array to object

I'm trying to filter an array of objects of people into a new object with its property names arranged by age each in a separate array.
Example 1
Input:
groupAdultsByAgeRange([{name: "Henry", age: 9}, {name: "John", age: 20}])
Result:
{ '20 and younger': [ { name: 'John', age: 20 } ] }
Example 2
Input:
groupAdultsByAgeRange([{name: "Anna", age: 31}, {name: "John", age: 32}, {name: "Hank", age: 60}])
Result:
{
'31-40': [ { name: 'Anna', age: 31 }, { name: 'John', age: 32 } ],
'51 and older': [ { name: 'Hank', age: 60 } ]
}
If the age range is not in the input than it should not be part of the output. For example if the input does not have people with age 51 than the object should not contain property of '51 and older'. and if input us empty array than output should be an empty object.
Using lodash you can do something like this
const _ = require('lodash');
const array = [{name: "Anna", age: 31}, {name: "John", age: 32}, {name: "Hank", age: 60}];
function ageToRange(obj) {
const age = obj.age;
if (age <= 20) {
return '20 and younger';
}
if (age > 30 && age <= 40) {
return '31-40'
}
if (age > 50) {
return '51 and older';
}
}
const result = _.groupBy(array, ageToRange)
console.log(result);
This is how you do with native Javascript :
//example 1
let people1 = [{name: "Henry", age: 9}, {name: "John", age: 20}]
let younger_and_equal_20 = []
people1.forEach(e => {
if(e.age>=20){
younger_and_equal_20.push(e)
}
});
console.log({"20 and younger": younger_and_equal_20})
//example 2
let people2 = [{name: "Anna", age: 31}, {name: "John", age: 32}, {name: "Hank", age: 60}]
let data = {
"31-40" : [],
"51 and older" : []
}
people2.forEach(e => {
if(e.age>30 && e.age<=40){
data["31-40"].push(e)
}
else if(e.age>50){
data["51 and older"].push(e)
}
});
console.log(data)
let result = {}
function groupAdultsByAgeRange(people) {
// An array of object which represents people who are 20 or younger
const ageGroup = people.filter(person => person.age <= 20)
// Only add array to the object key if it's not empty
if (ageGroup.length > 0) {
result["20 and younger"] = ageGroup
}
return result;
}

JavaScript array map while keeping original elements in the array

I have this array.
const data = [
{ name: "A", age: "12" },
{ name: "B", age: "5" },
{ name: "C", age: "6" }
];
I want to add additional property called key to this array like this.
const data = [
{ name: "A", age: "12", key: "A12" },
{ name: "B", age: "5", key: "B5" },
{ name: "C", age: "6", key: "C6" }
];
I tired this map function and output is not the one I expect. How do I achieve this using JS map function..
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
console.log(
"MAP",
data.map(element => (element.key = element.name + element.age))
);
You need to return an object from the map callback:
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
console.log(
data.map(({ name, age }) => ({ name, age, key: name + age }))
);
If you wish to mutate the existing objects, then use forEach instead of .map:
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
data.forEach((obj) => {
obj.key = obj.name + obj.age;
});
console.log(data);
Return element as well using the comma operator:
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
console.log(data.map(element => (element.key = element.name + element.age, element)));
.as-console-wrapper { max-height: 100% !important; top: auto; }
Or just change your map callback to make it simpler with destructuring:
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
console.log(data.map(({ name, age }) => ({ name, age, key: name + age})));
.as-console-wrapper { max-height: 100% !important; top: auto; }
There are more than one way to do this and the Array.map is by far the most concise and clean way to do it as already provided. Here are few other methods:
const data = [ { name: "A", age: "12" }, { name: "B", age: "5" }, { name: "C", age: "6" } ];
let from = Array.from(data, ({name, age}) => ({name, age, key: name+age}))
let assign = data.map(({name, age}) => Object.assign({name, age}, {key: name+age}))
console.log('from: ', from)
console.log('assign: ', assign)
If you want to generate a key from all values (assuming all are primitives) you can also do this:
const data = [
{ name: "A", age: "12", city: 'Miami' }, // "key": "A12Miami"
{ name: "B", age: "2", gender: 'M'}, // "key": "B2M"
];
let all = data.map(obj =>
({...obj, ...Object.fromEntries([['key', Object.values(obj).join('')]])}))
console.log(all)
This way you do not have to specify or care about the pop names etc.

Remove array duplicates by property javascript

I have a list of objects:
[{name: "bob", age: "14"}, {name: "bob", age: "16"}, {name: "sue", age: "21"}]
I need a simple way to filter out duplicates with regard to the name property, so:
[{name: "bob", age: "14"}, {name: "sue", age: "21"}]
There seem to be quite a few array duplicate removal questions, but not any based on a property. It can ignore all other fields.
Iterate the array, place all name values in a hash and skip objects whose name is already in the hash:
filterBy = function(ary, prop) {
var seen = {};
return ary.filter(function(item) {
var key = item[prop];
if(seen[key] === 1)
return false;
seen[key] = 1;
return true;
});
}
//
a = [{name: "bob", age: "14"}, {name: "bob", age: "16"}, {name: "sue", age: "21"}]
b = filterBy(a, 'name');
console.log(b);
ES6 version:
filterBy = function(ary, prop) {
var seen = new Set();
return ary.filter(item => !seen.has(item[prop]) && seen.add(item[prop]));
}
a = [{name: "bob", age: "14"}, {name: "bob", age: "16"}, {name: "sue", age: "21"}]
b = filterBy(a, 'name');
console.log(b);
You can do it with 2 for loops as follows. All you have to do is, keeping a result array and every time you insert into it, check whether the name attributes are equal.
function findDuplicate(){
var array= [{name: "bob", age: "14"}, {name: "bob", age: "16"}, {name: "sue", age: "21"}];
var result=[];
for(x in array){
var found=false;
for(y in result){
if(result[y].name.localeCompare(array[x].name)==0){
found=true;
}
}
if(!found){
result.push(array[x]);
}
}
console.log(result);
}
You could do this with forEach and thisArg param.
var data = [{name: "bob", age: "14"}, {name: "bob", age: "16"}, {name: "sue", age: "21"}];
var result = [];
data.forEach(function(e) {
if (!this[e.name]) {
this[e.name] = e;
result.push(this[e.name]);
}
}, {});
console.log(result)
Or with forEach and map()
var data = [{name: "bob", age: "14"}, {name: "bob", age: "16"}, {name: "sue", age: "21"}], result =[];
data.forEach(function(e) {
if(result.map(a => {return a.name}).indexOf(e.name) == -1 ) result.push(e);
});
console.log(result)
You could use Array#filter and a this object for marking, if the same name is already filtered.
var array = [{ name: "bob", age: "14" }, { name: "bob", age: "16" }, { name: "sue", age: "21" }],
filtered = array.filter(function (a) {
if (!this[a.name]) {
this[a.name] = true;
return true;
}
}, Object.create(null));
console.log(filtered);
For the straightforward comparison in the Q., there are some
good answers here. If you want to provide a custom comparison
function that will work on e.g. object values, or that uses
a RegExp, then take a look at the following.
var dedupwhen = function(fn, list){
if(list[0] === undefined){
return [];
}
// Join the first item to the remainder that has had the first
// item filtered out (according to fn) and then been
// deduplicated itself.
return [list[0]].concat(dedupwhen(fn, list.slice(1).filter(function(item){
return !fn(list[0], item);
})));
};
var similarname = function(x,y){
return RegExp('^' + x.name + '$', 'i').test(y.name);
};
var list = [
{name: 'Sue', age: 44},
{name: 'Bob', age: "14"},
{name: 'bob', age: "16"},
{name: 'sue', age: "21"}
];
console.log(dedupwhen(similarname, list));

Categories