Create unique values from duplicates in Javascript array of objects - javascript

I have an array of duplicated objects in Javascript. I want to create an array of unique objects by adding the index of occurrence of the individual value.
This is my initial data:
const array= [
{name:"A"},
{name:"A"},
{name:"A"},
{name:"B"},
{name:"B"},
{name:"C"},
{name:"C"},
];
This is expected end result:
const array= [
{name:"A-0"},
{name:"A-1"},
{name:"A-2"},
{name:"B-0"},
{name:"B-1"},
{name:"C-0"},
{name:"C-1"},
];
I feel like this should be fairly simple, but got stuck on it for a while. Can you please advise how I'd go about this? Also if possible, I need it efficient as the array can hold up to 1000 items.
EDIT: This is my solution, but I don't feel like it's very efficient.
const array = [
{ name: "A" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
];
const sortedArray = _.sortBy(array, 'name');
let previousItem = {
name: '',
counter: 0
};
const indexedArray = sortedArray.map((item) => {
if (item.name === previousItem.name) {
previousItem.counter += 1;
const name = `${item.name}-${previousItem.counter}`;
return { name };
} else {
previousItem = { name: item.name, counter: 0};
return item;
}
});

Currently you are sorting it first then looping over it, which may be not the most efficient solution.
I would suggest you to map over it with a helping object.
const a = [{name:"A"},{name:"A"},{name:"A"},{name:"B"},{name:"B"},{name:"C"},{name:"C"},], o = {};
const r = a.map(({ name }) => {
typeof o[name] === 'number' ? o[name]++ : o[name] = 0;
return { name: `${name}-${o[name]}` };
});
console.log(r);

Keep a counter, and if the current name changes, reset the counter.
This version mutates the objects. Not sure if you want a copy or not. You could potentially sort the array by object name first to ensure they are in order (if that's not already an existing precondition.)
const array = [
{ name: "A" },
{ name: "A" },
{ name: "A" },
{ name: "B" },
{ name: "B" },
{ name: "C" },
{ name: "C" },
];
let name, index;
for (let i in array) {
index = array[i].name == name ? index + 1 : 0;
name = array[i].name;
array[i].name += `-${index}`;
}
console.log(array);
Another way, if you don't want to sort, and don't want to mutate any objects, is to use a map and keep track of the current index for each object.
const array = [
// NOTE: I put the items in mixed up order.
{ name: "A" },
{ name: "C" },
{ name: "A" },
{ name: "B" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
];
let index = {};
let next = name => index[name] = index[name] + 1 || 0;
let result = array.map(obj => ({ ...obj, name: obj.name + '-' + next(obj.name) }));
console.log(result);

Related

How to exclude certain property from the list in NodeJS

I'm having the below list and I would like to add only these property names PRODUCT_TYPE, PRODUCT_TERM, PRODUCT_ID in myProduct. I want to ignore rest of the properties - I've around 100 properties and want to filter only a few of them from myProduct
Please find my code below:
const obj = {
myProduct: [
{
name: "PRODUCT_PRICE",
value: "234.324",
},
{
name: "PRODUCT_NAME",
value: "Insurance",
},
{
name: "PRODUCT_TYPE",
value: "Life",
},
{
name: "PRODUCT_TERM",
value: "Long",
},
{
name: "PRODUCT_ID",
value: "AP3232343JKD",
},
{
name: "PRODUCT_ENABLED",
value: "TRUE",
},
],
};
const allowedNames = [
'PRODUCT_TYPE',
'PRODUCT_TERM',
'PRODUCT_ID'
];
const updateCertainProperties = {
PRODUCT_ID: "app.productID",
PRODUCT_ENABLED: "app.product.enabled"
};
const productName = "testProduct_3234dfasfdk3msldf23";
const environment = obj.myProduct.map((o) => {
obj.myProduct.filter(product => allowedNames.includes(product.name));
if (updateCertainProperties[o.name]) o.name = updateCertainProperties[o.name];
if (o.name === "PRODUCT_NAME") o.value = productName;
return obj.myProduct;
});
console.log(obj.myProduct)
Expected output:
[
{ name: 'PRODUCT_NAME', value: 'testProduct_3234dfasfdk3msldf23' },
{ name: 'PRODUCT_TYPE', value: 'Life' },
{ name: 'PRODUCT_TERM', value: 'Long' },
{ name: 'app.productID', value: 'AP3232343JKD' },
{ name: 'app.product.enabled', value: 'TRUE' }
]
Can someone please help me how can I achieve this? Appreciated your help in advance!
You can create an array of allowed names and filter them out using includes()
css just for prettier output
UPDATE
added updateCertainProperties object values into allowedNames array and moved filter outside environment map.
const obj = {
myProduct: [
{
name: "PRODUCT_PRICE",
value: "234.324",
},
{
name: "PRODUCT_NAME",
value: "Insurance",
},
{
name: "PRODUCT_TYPE",
value: "Life",
},
{
name: "PRODUCT_TERM",
value: "Long",
},
{
name: "PRODUCT_ID",
value: "AP3232343JKD",
},
{
name: "PRODUCT_ENABLED",
value: "TRUE",
},
],
};
const allowedNames = [
'PRODUCT_TYPE',
'PRODUCT_TERM',
'PRODUCT_NAME'
];
const updateCertainProperties = {
PRODUCT_ID: "app.productID",
PRODUCT_ENABLED: "app.product.enabled"
};
allowedNames.push(...Object.values(updateCertainProperties));
const productName = "testProduct_3234dfasfdk3msldf23";
const environment = obj.myProduct.map((o) => {
if (updateCertainProperties[o.name]) o.name = updateCertainProperties[o.name];
if (o.name === "PRODUCT_NAME") o.value = productName;
return obj.myProduct;
});
obj.myProduct = obj.myProduct.filter(product => allowedNames.includes(product.name));
console.log(obj.myProduct)
.as-console-wrapper {
max-height: unset !important;
top: 0;
}
It sounds like you're describing filtering an array, not "excluding properties". You have an array of objects, with each object consisting of a name property and value property. And you only want objects with specific values in their name property.
Using .filter on the array, it might look something like this:
obj.myProduct = obj.myProduct.filter(p => (
p.name === 'PRODUCT_TYPE' ||
p.name === 'PRODUCT_TERM' ||
p.name === 'PRODUCT_ID'));
This would filter out all elements of the array which don't match the supplied condition.

adding another key and value to an object inside an array with JS

So I currently have a bunch of objects inside an array like below. However, I'm now trying to write a function that allows me to add another key|value into the object that was added last.
My current idea is using the arrayname.length - 1 to work out the position of the object within the array.
Would I need to create a temporary array to store the new object and then set (tempArray = oldArray) at the end of the function or would I concatinate them both?
const state = [
{
userId: 1,
},
{
Name: name,
},
{
age: 52,
},
{
title: "et porro tempora",
}]
this is the current code
let objects = [];
const addParent = (ev) =>{
ev.preventDefault();
// getting the length of the objects array
let arrayLength = objects.length;
// if the length of the array is zero - empty or one then set it to default zero
// else if there is objects stored in the array minus 1 to get the array position
if(arrayLength <= 0){
arrayLength = 0;
}else{
arrayLength = objects.length - 1;
}
//make a temporary array to be able to push new parent into an existing object
var tempObjects = []
for (var index=0; index<objects.length; index++){
}
//create a new parent object key : value
let parent = {
key: document.getElementById('key').value,
value: document.getElementById('value').value
}
//push parent object key and value into object
//objects.push(parent);
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('btn1').addEventListener('click', addParent);
});
There are multiple ways to do this.
Try this one
var objects = [{
name: "1"
}];
const addParent = (ev) => {
let parent = {
key: "some value",
value: "some value"
}
objects = Array.isArray(objects) ? objects : [];
let lastObjectIndex = objects.length - 1;
lastObjectIndex = lastObjectIndex > -1 ? lastObjectIndex : 0
objects[lastObjectIndex] = { ...objects[lastObjectIndex],
...parent
}
}
I think You want to add new value to last object of the array
Method 1
const state = [
{
userId: 1,
},
{
Name: name,
},
{
age: 52,
},
{
title: "et porro tempora",
}]
state[state.length - 1].newKey = "value"
console.log(state)
Method 2
const state = [
{
userId: 1,
},
{
Name: name,
},
{
age: 52,
},
{
title: "et porro tempora",
}]
// 1st method
state[state.length - 1] = {
...state[state.length - 1] ,
newKey : "value"
}
console.log(state)
You can probably use something like this:
const a = [
{ "a" : "a" },
{ "b" : "b" }
]
const c = a.map((obj, index) => {
if (index === a.length -1) {
return { ...obj, newProp: "newProp" }
}
return obj;
});
console.log(c)
This will add property on the last object using spread operator, you can look it up if you are new to JS but basically it will retain all the existing property and add the newProp to the object

Set new Sequence in Object from Array after deletion of specific object

My Goal:
I need to have a continuous sequence of numbers in the sequenceIndex which is a value in my object.
So when I remove a specific object the sequence index of the other objects is of course not continuous anymore. The object I remove is being checked against a specific value to see whether there are other objects in the array which share the same value (second if-statement). If so then there should be a new value set which is continuous.
The output is that the iterator in the if-statement is always the same for all objects manipulated.
From this:
const objectsArray = [
{
folder: "folderName",
documents: [
{
id: 0,
sequenceIndex: "0",
documentType: "letter"
},
{
id: 1,
sequenceIndex: "1",
documentType: "letter"
},
{
id: 2,
sequenceIndex: "2",
documentType: "letter"
},
{
id: 3,
sequenceIndex: "3",
documentType: "letter"
}
]
}
];
By removing id 1 and 2 I would like to come to this (see continuous sequenceIndex):
const desiredObjectsArray = [
{
folder: "folderName",
documents: [
{
id: 0,
sequenceIndex: "0",
documentType: "letter"
},
{
id: 3,
sequenceIndex: "1",
documentType: "letter"
}
]
}
];
My code so far:
case ActionType.RemoveDocumentInSpecificFolder:
return state.map(file => {
// if in the correct folder remove the object with the delivered id
if (file.folder=== folder) {
remove(file.documents, {
id: action.payload.documents[0].id
});
// create newObjArray from objects which share a specific value and replace the sequence index by new value
const newObjArray = file.documents.map((obj: any) => {
// if the object has the specific value create new object with new sequenceIndex
if (obj.documentType === action.payload.documents[0].documentType) {
//poor attempt to create a sequence
let i = 0;
const correctedSequenceDocObject = { ...obj, sequenceIndex: i };
i++;
return correctedSequenceDocObject;
}
return {
...obj
};
});
return {
...file,
documents: newObjArray
};
}
return file;
});
I hope someone can guide me in the right direction. I would also always appreciate a suggestion of best practice :)
Best regards
You can use filter and map something like this
const arr = [{folder: "folderName",documents: [{id: 0,sequenceIndex: "0",documentType: "letter"},{id: 1,sequenceIndex: "1",documentType: "letter"},{id: 2,sequenceIndex: "2",documentType: "letter"},{id: 3,sequenceIndex: "3",documentType: "letter"}]}];
let getInSequence = (filterId) => {
return arr[0].documents.filter(({ id }) => !filterId.includes(id))
.map((v, i) => ({ ...v, sequenceIndex: i }))
}
console.log(getInSequence([1, 2]))
As commented:
This is the classic case where .filter().map() will be useful. filter the data and then use .map((o, i) => ({ ...obj, sequenceIndex: i+1 }) )
Following is the sample:
const objectsArray = [{
folder: "folderName",
documents: [{
id: 0,
sequenceIndex: "0",
documentType: "letter"
},
{
id: 1,
sequenceIndex: "1",
documentType: "letter"
},
{
id: 2,
sequenceIndex: "2",
documentType: "letter"
},
{
id: 3,
sequenceIndex: "3",
documentType: "letter"
}
]
}];
const ignoreIds = [1, 2]
const updatedDocs = objectsArray[0].documents
.filter(({
id
}) => !ignoreIds.includes(id))
.map((doc, index) => ({ ...doc,
sequenceIndex: index
}));
console.log(updatedDocs)
Now lets cover your attempt
const newObjArray = file.documents.map((obj: any) => {
// For all the unmatching objects, you will have undefined as object as you are using `.map`
// This will make you `newObjArray: Array<IDocument | undefined>` which can break your code.
if (obj.documentType === action.payload.documents[0].documentType) {
// This will set it as 0 in every iteration making i as 0 always.
let i = 0;
const correctedSequenceDocObject = { ...obj, sequenceIndex: i };
i++;
return correctedSequenceDocObject;
}
return { ...obj };
});
An alternate with single loop:
Idea:
Create a loop using Array.reduce and pass it a blank array as list.
Add a check and inside it, push value to this list.
For sequenceIndex, fetch last element and fetch its sequenceIndex. Add one and set it again.
const newObjArray = file.documents.reduce((acc: Array<IDocument>, obj: any) => {
if (obj.documentType === action.payload.documents[0].documentType) {
const sequenceIndex: number = (!!acc[acc.length - 1] ? acc[acc.length - 1].sequenceIndex : 1) + 1;
acc.push({ ...obj, sequenceIndex });
}
return acc;
});
The solution I used now to this problem was:
let count = 0;
const newObject = file.documents.map(obj => {
if (obj.documentType === firstDocument.documentType) {
count++;
return { ...obj, sequenceIndex: count - 1 };
}
return obj;
});
Both of the provided answers were not able to handle objects which were out of interest because of the different documentType so they dropped the object. with this solution, I am checking against the last element and increasing the count if the last element was the same documentType.

How to add a value to an array in a specific location with JS

I have an array of objects:
const array = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 }
];
and I need to add another entry to it, but it needs to be placeable within any location in the array. So for example:
array.push({ id: 5, after_id: 2 }); and this should place the new entry between ids 2 and 3. Is there some standard way of doing this?
#p.s.w.g Has posted what is probably the best solution in a comment already, but I thought I'd post my original solution here as an answer now this is reopened.
You can use some to iterate through the array until the correct index is found, then you can slice the array and insert the item at the relevant index:
const arrayTest = [{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
const insertAfterId = (array, item, idAfter) => {
let index = 0;
array.some((item, i) => {
index = i + 1;
return item.id === idAfter
})
return [
...array.slice(0, index),
item,
...array.slice(index, array.length),
];
};
const result = insertAfterId(arrayTest, {
id: 6
}, 2)
console.dir(result)

Create new array from iterating JSON objects and getting only 1 of its inner array

See jsfiddle here: https://jsfiddle.net/remenyLx/2/
I have data that contains objects that each have an array of images. I want only the first image of each object.
var data1 = [
{
id: 1,
images: [
{ name: '1a' },
{ name: '1b' }
]
},
{
id: 2,
images: [
{ name: '2a' },
{ name: '2b' }
]
},
{
id: 3
},
{
id: 4,
images: []
}
];
var filtered = [];
var b = data1.forEach((element, index, array) => {
if(element.images && element.images.length)
filtered.push(element.images[0].name);
});
console.log(filtered);
The output needs to be flat:
['1a', '2a']
How can I make this prettier?
I'm not too familiar with JS map, reduce and filter and I think those would make my code more sensible; the forEach feels unnecessary.
First you can filter out elements without proper images property and then map it to new array:
const filtered = data1
.filter(e => e.images && e.images.length)
.map(e => e.images[0].name)
To do this in one loop you can use reduce function:
const filtered = data1.reduce((r, e) => {
if (e.images && e.images.length) {
r.push(e.images[0].name)
}
return r
}, [])
You can use reduce() to return this result.
var data1 = [{
id: 1,
images: [{
name: '1a'
}, {
name: '1b'
}]
}, {
id: 2,
images: [{
name: '2a'
}, {
name: '2b'
}]
}, {
id: 3
}, {
id: 4,
images: []
}];
var result = data1.reduce(function(r, e) {
if (e.hasOwnProperty('images') && e.images.length) r.push(e.images[0].name);
return r;
}, [])
console.log(result);
All answers are creating NEW arrays before projecting the final result : (filter and map creates a new array each) so basically it's creating twice.
Another approach is only to yield expected values :
Using iterator functions
function* foo(g)
{
for (let i = 0; i < g.length; i++)
{
if (g[i]['images'] && g[i]["images"].length)
yield g[i]['images'][0]["name"];
}
}
var iterator = foo(data1) ;
var result = iterator.next();
while (!result.done)
{
console.log(result.value)
result = iterator.next();
}
This will not create any additional array and only return the expected values !
However if you must return an array , rather than to do something with the actual values , then use other solutions suggested here.
https://jsfiddle.net/remenyLx/7/

Categories

Resources