Javascript function to modify the array of objects reuturns the same array - javascript

I have a data inside of my array, which looks like this.
const arr = [
{
devSth: {
h1: [1],
h2: [1],
}
},
];
I have a function, which should modify this array. It looks like this:
const modifyArr = (data) => {
data.forEach(el => {
let keys = el.devSth && Object.keys(el.devSth);
keys.forEach(key => {
el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
})
})
})
return data;
}
Then I am using my function:
const newArr = modifyArr(arr);
console.log(newArr);
Expected output:
[{
devSth: {
h1: [{name: 1, val: 'lalala'}],
h2: [{name: 1, val: 'lalala'}]
}
}]
Actual output:
[{
devSth: {
h1: [1],
h2: [1]
}
}]
Would appreciate any help.

you are mapping the values, but not setting them (line 5) :
const modifyArr = (data) => {
data.forEach(el => {
let keys = el.devSth && Object.keys(el.devSth);
keys.forEach(key => {
el.devSth[key] = el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
})
})
})
return data;
}
this is untested.

You dont assign the result of the .map operation back to the original keys h1 and h2. So it doesn't modify those.
Just change
el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
})
to
el.devSth[key] = el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
const arr = [{
devSth: {
h1: [1],
h2: [1],
}
}, ];
const modifyArr = (data) => {
data.forEach(el => {
let keys = el.devSth && Object.keys(el.devSth);
keys.forEach(key => {
el.devSth[key] = el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
})
})
})
return data;
}
const newArr = modifyArr(arr);
console.log(newArr);

An alternative way of doing it:
const arr = [{
devSth: {h1: [1], h2: [1]}
}];
let obj = arr[0].devSth, number;
for (const prop in obj){
number = obj[prop][0];
obj[prop] = [{name: number, val: 'lalala'}];
}
console.log([{devSth: obj}])

Related

In an array of objects how to group objects which have same value and include the values that differ [duplicate]

This question already has answers here:
Group array items using object
(19 answers)
Closed last year.
I have an array of objects and I would like to group the objects which have same name and make an array containing the other values which differs. How can I achieve that?
const arr = [
{
name: 'A',
color: 'blue',
},
{
name: 'A',
color: 'purple',
},
{
name: 'B',
color: 'Yellow',
},
{
name: 'B',
color: 'Green',
},
];
What I would like to get:
const result = [
{
name: 'A',
color: ['blue', 'purple'],
},
{
name: 'B',
color: ['Yellow', 'Green'],
},
];
This looks like something reduce() should be used for.
Use find() to find in the existing array element based on some condition.
If element exists, push into colors property of the element.
Else push into the array a new object.
const arr = [
{
name: 'A',
color: 'blue',
},
{
name: 'A',
color: 'purple',
},
{
name: 'B',
color: 'Yellow',
},
{
name: 'B',
color: 'Green',
},
];
let ans = arr.reduce((agg,curr) => {
let found = agg.find((x) => x.name === curr.name);
if(found){
found.colors.push(curr.color);
}
else{
agg.push({
name : curr.name,
colors : [curr.color]
});
}
return agg;
},[]);
console.log(ans);
const found = acc.find(item => item.name === curr.name);
if (found) {
found.color.push(curr.color);
} else {
acc.push({
name: curr.name,
color: [curr.color],
});
}
return acc;
}
, []);
Here is one way to do it:
const arrNames = Array.from(new Set(arr.map((x) => x.name))); // make an array of unique names
const result = arrNames
.map((x) => arr.filter((y) => y.name === x)) // filter by name
.map((x, i) => ({ name: arrNames[i], color: x.map((y) => y.color) })); // make new objects
Create set of props then loop over possible names and filter their values O(n^2)
const set = new Set(arr.map((obj) => obj.name));
const res = [];
for(const name of set.keys()) {
const colors = arr.filter((obj) => obj.name === name).map((obj) => obj.color);
res.push({name, colors});
}
Or create a dictionary whose keys will be name-s, and values - array O(n)
const mp = new Map();
for (const obj of arr) {
if (mp.has(obj.name)) {
mp.get(obj.name).push(obj.color);
} else {
mp.set(obj.name, [obj.color]);
}
}
const result = [];
for (const [name, color] of mp.entries()) {
result.push({name, color});
}
let results = [];
const arr = [
{
name: "A",
color: "blue",
},
{
name: "A",
color: "purple",
},
{
name: "B",
color: "Yellow",
},
{
name: "B",
color: "Green",
},
];
const names = arr.map((element) => element.name);
const uniqueNames = [...new Set(names)];
uniqueNames.forEach((element) => {
let temp = {};
temp.name = element;
temp.color = [];
arr.forEach((element2) => {
if (element === element2.name) {
temp.color.push(element2.color);
}
});
results.push(temp);
});
console.log("results", results);

Change value from an array

I have the next code:
const arr = [
{
name:'john',
cars:[
{audi:1},
{bmw:2}
]
},
{
name:'bill',
cars:[
{audi:10},
{bmw:0}
]
}
]
const arr1 = arr.map(i => {
if(i.name === 'john') {
return i.cars.map( a => {
return {
...i,
test:[2]
}
})
}
return i
})
console.log(arr1)
Here i want too loop through the array and for the first object to change the cars array, adding test:[2]. For this i used:
const arr1 = arr.map(i => {
if(i.name === 'john') {
return i.cars.map( a => {
return {
...i,
test:[2]
}
})
}
return i
})
The issue is that my code don't return what i want. I get the first object like:
0: Object
name: "john"
cars: Array[2]
test: 2
1: Object
name: "john"
cars: Array[2]
test: 2
but i need like this:
{
name:'john',
cars:[
{
audi:1,
test: [2],
},
{bmw:2}
]
},
How to solve my issue?
Since you only want to change the first item in the cars array, I don't think map is right - instead, just list the first changed car as an object literal inside an array, then spread the remaining cars into the array with .slice(1):
const arr = [
{
name:'john',
cars:[
{audi:1},
{bmw:2}
]
},
{
name:'bill',
cars:[
{audi:10},
{bmw:0}
]
}
]
const arr1 = arr.map(person => (
person.name !== 'john'
? person
: ({
name: person.name,
cars: [
{ ...person.cars[0], test: [2] },
...person.cars.slice(1)
]
})
));
console.log(arr1)
You could address the right position and add the wanted property.
const
data = [{ name: 'john', cars: [{ audi: 1 }, { bmw: 2 }] }, { name: 'bill', cars: [{ audi: 10 }, { bmw: 0 }] }],
add = { target: [0, 0], value: { test: [2] } }
result = data.map((o, i) => i === add.target[0]
? { ...o, cars: o.cars.map((p, j) => j === add.target[1]
? {... p, ...add.value }
: p)
}
: o);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Given the desired result, a non mapping solution may be viable?
const arr = [{
name: 'john',
cars: [{
audi: 1
},
{
bmw: 2
}
]
},
{
name: 'bill',
cars: [{
audi: 10
},
{
bmw: 0
}
]
}
];
// clone the initial arr
const arrModified = Object.assign([], arr);
// find John
const indexJohn = arrModified.findIndex(v => v.name === "john");
if (indexJohn > -1) {
// modify the desired value
arrModified[indexJohn].cars[0].test = [2];
}
console.log(arrModified[0].cars);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Get list of duplicate objects in an array of objects

I am trying to get duplicate objects within an array of objects. Let's say the object is like below.
values = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' },
{ id: 11, name: 'someName3' },
{ id: 12, name: 'someName4' }
];
Duplicate objects should return like below:
duplicate = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' }
];
You can use Array#reduce to make a counter lookup table based on the id key, then use Array#filter to remove any items that appeared only once in the lookup table. Time complexity is O(n).
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const lookup = values.reduce((a, e) => {
a[e.id] = ++a[e.id] || 0;
return a;
}, {});
console.log(values.filter(e => lookup[e.id]));
Let's say you have:
arr = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
So, to get unique items:
unique = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) === i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]);
Then, result will be
unique = [
{ id:10, name: 'someName1' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
And, to get duplicate ids:
duplicateIds = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) !== i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]["id"])
List of IDs will be
duplicateIds = [10]
Thus, to get duplicates objects:
duplicate = arr.filter(obj=> dublicateIds.includes(obj.id));
Now you have it:
duplicate = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' }
]
Thanks https://reactgo.com/removeduplicateobjects/
You haven't clarified whether two objects with different ids, but the same "name" count as a duplicate. I will assume those do not count as a duplicate; in other words, only objects with the same id will count as duplicate.
let ids = {};
let dups = [];
values.forEach((val)=> {
if (ids[val.id]) {
// we have already found this same id
dups.push(val)
} else {
ids[val.id] = true;
}
})
return dups;
With lodash you can solve this with filter and countBy for complexity of O(n):
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const counts = _.countBy(data, 'id')
console.log(_.filter(data, x => counts[x.id] > 1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
You could do the same with ES6 like so:
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const countBy = (d, id) => d.reduce((r,{id},i,a) => (r[id] = a.filter(x => x.id == id).length, r),{})
const counts = countBy(data, 'id')
console.log(data.filter(x => [x.id] > 1))
You can use an array to store unique elements and use filter on values to only return duplicates.
const unique = []
const duplicates = values.filter(o => {
if(unique.find(i => i.id === o.id && i.name === o.name)) {
return true
}
unique.push(o)
return false;
})
With lodash you can use _.groupBy() to group elements by their id. Than _.filter() out groups that have less than two members, and _.flatten() the results:
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const result = _.flow([
arr => _.groupBy(arr, 'id'), // group elements by id
g => _.filter(g, o => o.length > 1), // remove groups that have less than two members
_.flatten // flatten the results to a single array
])(values);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
An alternative based in #ggorlen solution with new Map() as accumulator (for better performance) and without unary operator ++ (not advised by default in projects with ESLint).
const values = [{ id: 10, name: "someName1" }, { id: 10, name: "someName2" }, { id: 11, name: "someName3" }, { id: 12, name: "someName4" },];
const lookup = values.reduce((a, e) => {
a.set(e.id, (a.get(e.id) ?? 0) + 1);
return a;
}, new Map());
console.log(values.filter(e => lookup.get(e.id) > 1));
Try this
function checkDuplicateInObject(propertyName, inputArray) {
var seenDuplicate = false,
testObject = {};
inputArray.map(function(item) {
var itemPropertyName = item[propertyName];
if (itemPropertyName in testObject) {
testObject[itemPropertyName].duplicate = true;
item.duplicate = true;
seenDuplicate = true;
}
else {
testObject[itemPropertyName] = item;
delete item.duplicate;
}
});
return seenDuplicate;
}
referred from : http://www.competa.com/blog/lets-find-duplicate-property-values-in-an-array-of-objects-in-javascript/

Updating selection of array's values

I have two arrays. Each array could have a different number of objects but they each have the same properties but could have different values. For example
var Array1 = [ { id: '1', value: a },
{ id: '2', value: b } ]
var Array2 = [ { id: '', value: c },
{ id: '', value: d },
{ id: '', value: a } ]
What I want
AfterArray = [ { id: '1', value: a },
{ id: '3', value: c },
{ id: '4', value: d } ]
What's happening is that array1's object will be removed if it doesn't have array2's value. If it does have array2's value, it will keep the original id. If an object is in array2 that isn't in array1, an id will be generated (UUID).
I'm assuming it might go something like this
afterArray = []
this.Array1.forEach((res, i) => {
this.Array2.forEach((res2, 2) => {
if(res.value == res2.value){
afterArray = afterArray.concat(this.Array1[i])
}
else {
// do something if values are not present then add to array.
// if added, add id to those empty properties.
}
})
})
Thanks!
You just need a simple mapping over Array2 with a find inside it, to find the matching value in Array1 if it exists:
const array1 = [
{
id: '1',
value: 'a'
},
{
id: '2',
value: 'b'
}
];
const array2 = [
{
id: '',
value: 'c'
},
{
id: '',
value: 'd'
},
{
id: '',
value: 'a'
}
];
const generateId = (() => {
// example generator function, use your own instead
let possibleIds = ['3', '4'];
let i = -1;
return () => {
i++;
return possibleIds[i];
};
})();
const result = array2.map(({ id, value }) => {
// find a matching value in array1 to merge the id:
const foundArr1Item = array1.find(({ value: ar1Val }) => ar1Val === value);
// otherwise, generate a new ID:
if (foundArr1Item) return { value, id: foundArr1Item.id };
return { value, id: generateId() };
});
console.log(result);
If I understood it right, this should do your job:
(find the comments in the code)
Array1 = [
{
id: '1',
value: "a"
},
{
id: '2',
value: "b"
}
]
Array2 = [
{
id: '',
value: "c"
},
{
id: '',
value: "d"
},
{
id: '',
value: "a"
}
]
// keep Array1's objects if it has a value matching a value from any Array2 object
// Also remove those objects from Array2
newArray1 = Array1.reduce((acc, elem) => {
let indexOfObInArray2 = Array2.findIndex(eachArray2Elem => {
return elem.value == eachArray2Elem.value
});
if (indexOfObInArray2 > -1) {
acc.push(elem);
Array2.splice(indexOfObInArray2, 1);
}
return acc;
}, [])
// Array of ids already taken by Objects from Array2, if they are non empty
idsTakenInArray2 = Array2.reduce((acc, x) => {
if (x.id != "") {
acc.push(x.id);
}
return acc;
}, []);
// random number to give ids
randomId = 1;
Array2 = Array2.map(eachElem => {
if (eachElem.id == '') {
while (Array1.find(eachArray1Elem => {
return eachArray1Elem.id == randomId
}) || idsTakenInArray2.indexOf(randomId) !== -1) {
randomId++;
}
eachElem.id = randomId;
idsTakenInArray2.push(randomId);;
}
return eachElem;
})
console.log(newArray1.concat(Array2));
check this, here is the code online https://stackblitz.com/edit/angular-zhzuqk , check your console you will see what you want to have as result
formtarrays(array1,array2) {
let ar = array1.concat(array2);
// delete items that exist in array1 but not in array2
ar = ar.filter((elem) => {
return !(array1.findIndex(item => item.value === elem.value) !== -1 && array2.findIndex(item => item.value === elem.value) === -1)
})
// get distinct values
const idList = [];
const distinct = [];
ar.forEach((item, index) => {
if (item !== undefined) {
idList['id'] = item.value;
if (idList.indexOf(item.value) < 0) {
if(item.id === '') {
item.id = (index + array1.length).toString();
}
distinct.push(item);
idList.push(item.value);
}
}
})
console.log(distinct);
return distinct;
}

Converting lodash _.uniqBy() to native javascript

Here in this snippet i am stuck as in _.uniqBy(array,iteratee),this
iteratee can be a function or a string at the same time
Where to put the condition to check uniqness on the property because itratee function can be anything
var sourceArray = [ { id: 1, name: 'bob' },
{ id: 1, name: 'bill' },
{ id: 1, name: 'bill' } ,
{id: 2,name: 'silly'},
{id: 2,name: 'billy'}]
function uniqBy (inputArray, callback) {
return inputArray.filter(callback)
}
var inputFunc = function (item) {
return item.name
}
// var destArray = _.uniqBy(sourceArray,'name')
var destArray = uniqBy(sourceArray, inputFunc)
console.log('destArray', destArray)
Any leads on this will be most appreciated.
An ES6 uniqBy using Map with a complexity of O(n):
const uniqBy = (arr, predicate) => {
const cb = typeof predicate === 'function' ? predicate : (o) => o[predicate];
return [...arr.reduce((map, item) => {
const key = (item === null || item === undefined) ?
item : cb(item);
map.has(key) || map.set(key, item);
return map;
}, new Map()).values()];
};
const sourceArray = [
{ id: 1, name: 'bob' },
{ id: 1, name: 'bill' },
null,
{ id: 1, name: 'bill' } ,
{ id: 2,name: 'silly'},
{ id: 2,name: 'billy'},
null,
undefined
];
console.log('id string: ', uniqBy(sourceArray, 'id'));
console.log('name func: ', uniqBy(sourceArray, (o) => o.name));
Refactored #ori-drori's solution and removed
undefined
null
extra numbers in mixed array
return [] if first param is not Array
const uniqBy = (arr, predicate) => {
if (!Array.isArray(arr)) { return []; }
const cb = typeof predicate === 'function' ? predicate : (o) => o[predicate];
const pickedObjects = arr
.filter(item => item)
.reduce((map, item) => {
const key = cb(item);
if (!key) { return map; }
return map.has(key) ? map : map.set(key, item);
}, new Map())
.values();
return [...pickedObjects];
};
const a = [
12,
undefined,
{ id: 1, name: 'bob' },
null,
{ id: 1, name: 'bill' },
null,
undefined
];
const b = [
12,
{ id: 1, name: 'bob' },
{ id: 1, name: 'bill' },
];
uniqBy(a, 'name');
uniqBy(b, Math.floor);
uniqBy([2.1, 1.2, 2.3], Math.floor);
I'm running my code through Webpack via CreateReactApp, it must be using a polyfill for spread that uses slice. Here's what I did instead, a variation of #oridori's answer:
const uniqBy = (arr: any[], predicate: (item: any) => string) => {
const cb = typeof predicate === 'function' ? predicate : (o) => o[predicate];
const result = [];
const map = new Map();
arr.forEach((item) => {
const key = (item === null || item === undefined) ? item : cb(item);
if (!map.has(key)) {
map.set(key, item);
result.push(item);
}
});
return result;
};
For those looking for the one line answer
var inputArr = [
{ id: 1, name: { first: 'bob' } },
{ id: 1, name: { first: 'bill' } },
{ id: 1, name: { first: 'bill' } },
{ id: 1 },
undefined,
{ id: 2, name: { first: 'silly' } },
{ id: 2, name: { first: 'billy' } }
]
var uniqBy = (arr, key) => {
return Object.values(arr.reverse().reduce((m, i) => {m[key.split('.').reduce((a, p) => a?.[p], i)] = i; return m;}, {}))
}
console.log(uniqBy(inputArr, 'id'))
console.log(uniqBy(inputArr, 'name.first'))
You could use a sort ordered by name and a filter based on the neighborhood comparison like this :
var sourceArray = [ { id: 1, name: 'bob' },
{ id: 1, name: 'bill' },
{ id: 1, name: 'bill' } ,
{id: 2,name: 'silly'},
{id: 2,name: 'billy'}]
var uniqBy = (inputArray, callback) => inputArray.sort((a,b) => callback(a) > callback(b))
.filter((x,i,arr) => i === arr.length -1 ? true : callback(x) !== callback(arr[i+1]));
var inputFunc = item => item.name;
var destArray = uniqBy(sourceArray, inputFunc)
console.log('destArray', destArray)

Categories

Resources