I find the following answer help me a lot in removing duplicate object array which contains duplicates.
I've made a fork of the example which I modified.
The function related:
const uniqueArray = things.thing.filter((thing,index) => {
return index === things.thing.findIndex(obj => {
return JSON.stringify(obj) === JSON.stringify(thing);
});
});
For example I have:
[
{"place":"here","name":"stuff"},
{"place":"there","name":"morestuff"},
{"place":"there","name":"morestuff"},
{"place":"herehere","name":"stuff"}
]
It would return:
[
{"place":"here","name":"stuff"},
{"place":"there","name":"morestuff"},
{"place":"herehere","name":"stuff"}
]
How to remove the repeating place name which contains the same name?
Expected output:
[
{"place":"here","name":"stuff"},
{"place":"there","name":"morestuff"}
]
You can reduce over the array of objects. Simply, if an object with a key value the same as the current object already exists in the accumulator, don't add it again.
Here's a function that allows you specify which key you want to dedupe:
const arr = [
{"place":"here","name":"stuff"},
{"place":"there","name":"morestuff"},
{"place":"there","name":"morestuff"},
{"place":"herehere","name":"stuff"}
];
// Accepts an array and a key that should have the
// duplicates removed
function remove(arr, key) {
// Iterate over the array passing in the accumulator
// and the current element
return arr.reduce((acc, c) => {
// If there is an object in the accumulator with the
// same key value as the current element simply return the
// accumulator
if (acc.find(obj => obj[key] === c[key])) return acc;
// Otherwise add the current element to the accumulator
// and return it
return acc.concat(c);
}, []);
}
function showJSON(arr, id) {
const json = JSON.stringify(arr, null, 2);
document.querySelector(`#${id} code`).textContent = json;
}
// remove duplicate places
showJSON(remove(arr, 'place'), 'places');
// remove duplicate names
showJSON(remove(arr, 'name'), 'names');
<div id="places">
Removed duplicate places
<pre><code></code></pre>
</div>
<div id="names">
Removed duplicate names
<pre><code></code></pre>
</div>
Check this
const things = [
{"place":"here","name":"stuff"},
{"place":"there","name":"morestuff"},
{"place":"there","name":"morestuff"},
{"place":"herehere","name":"stuff"}
]
const uniqueArray = things.reduce((accumulator, currentValue) => {
if (accumulator.find(a => a.name === currentValue.name))
return accumulator;
else
return (accumulator.push(currentValue), accumulator);
}, []);
Output
[ { place: 'here', name: 'stuff' },
{ place: 'there', name: 'morestuff' } ]
You can use array reduce with filter
let data=[
{"place":"here","name":"stuff"},
{"place":"there","name":"morestuff"},
{"place":"there","name":"morestuff"},
{"place":"herehere","name":"stuff"}
]
// Using reduce() to separate the contents we want
let result=data.reduce((acc,value)=>{
if(acc.filter(val=>val.name==value.name).length==0) // checking the accumulator if it already containsa the value
{
acc.push(value); // if the array returned is of length==0 we can push in it
}
return acc;
},[])
console.log(result);
See Array Filter, Array.prototype.Reduce
Related
I have an array arr1 and an object arr2 with a nested config array.
If the object of arr1 has a key that matches with an id in that nested config and deeper questions array, then change that key (in the arr1 object) with the title property that is found next to that id.
Here is an example. The key isWorking from arr1 is the same as arr2.config[0].questions[0].id value, so
change that isWorking key to the value found in arr2.config[0].questions[0].custom.title.
var arr1= [
{"jobs": "Marketing","isWorking": yes,"country": "MY"},
{"country": "IN","members": 4}
]
var arr2=
{
"id":1,
"name":"xxx",
"config":[
{
"questions":[
{
"id":"isWorking",
"custom":{
"title":"Are you working?"
}
},
{
"id":"jobs",
"custom":{
"title":"Please specify job(s)"
}
}
]
},
{
"questions":[
{
"id":"country",
"custom":{
"title":"which Country?"
}
},
{
"id":"members",
"type":"choices",
"custom":{
"title":"How many members?"
}
}
]
}
]
}
Expected output:
[
{"Please specify job(s)": "Marketing","Are you working": yes,"which Country": "MY"},
{"which Country": "IN","How many members": 4}
]
I tried:
var result = arr1.map(e => ({
arr2.config.find(i => {
i.questions.find( q => {
q.id === Object.key(e) ? Object.key(e) === q.custom.title : q.id
}
})
}))
In your code the find callbacks do not return anything. When you have a code block (with braces) you need a return statement. Also, the outer object literal which you have as the return value of the map callback cannot have just the find call in it. It should have the proper object literal syntax, like with spread syntax. Moreover, the find method can only return an existing object, not a new, modified one.
I will assume here that the matching strings for the first object have to be found in the first questions array, and for the second object in the second questions array.
I also propose to rename arr2, because it isn't an array. It is a plain object, with a property that is an array (config).
Here is how you could do it with Object.fromEntries and Object.entries:
const arr1 = [{"jobs": "Marketing","isWorking": "yes","country": "MY"}, {"country": "IN","members": 4}];
const obj = {"id":1,"name":"xxx","config":[{"questions":[{"id":"isWorking","custom":{"title":"Are you working?"}},{"id":"jobs","custom":{"title":"Please specify job(s)"}}]},{"questions":[{"id":"country","custom":{"title":"which Country?"}},{"id":"members","type":"choices","custom":{"title":"How many members?"}}]}]}
const result = arr1.map((e, i) => Object.fromEntries(
Object.entries(e).map(([key, value]) =>
[obj.config[i].questions.find(({id}) =>
key === id
)?.custom?.title ?? key, value]
)
))
console.log(result);
i'm new here, i have problem that i can not solve.
I have 2 different arrays:
The first array - contains ratings of users with their ID name
[
{"handle":"frontend1", "_redis":"3", "_nodejs":"5", "_mysql":"2", "_python":"3", "_mongo":"4"},
{"handle":"frontend3", "_php":"4", "_mysql":"4", "_oracle":"4", "_ruby":"3", "_mongo":"5", "_python":"5"},
{"handle":"frontend4", "_java":"5", "_ruby":"5", "_mysql":"5", "_mongo":"5"}
]
The second set - contains the ratings, which I want to return to each user.
If there is a rating that is not in the second set, I will not return it
In the second set, values do not matter, only keys
[
"_assembler",
"_css",
"_python",
"_php"
]
I want to return to the first set, the handle, and all the rankings that exist in the second set.
[
{"handle":"frontend1", "_python":"3" },
{"handle":"frontend3", "_php":"4", "_python":"5" },
{"handle":"frontend4"}
]
this is what i try to do.
keys = [
"_assembler",
"_css",
"_python",
"_php"
]
source = [
{"handle":"frontend1", "_redis":"3", "_nodejs":"5", "_mysql":"2", "_python":"3", "_mongo":"4"},
{"handle":"frontend3", "_php":"4", "_mysql":"4", "_oracle":"4", "_ruby":"3", "_mongo":"5", "_python":"5"},
{"handle":"frontend4", "_java":"5", "_ruby":"5", "_mysql":"5", "_mongo":"5"}
];
result = [];
tmp = {};
source.forEach((item) => {
Object.keys(item).map(({key,value}) =>
{
if(key == "handle")
{
tmp[key]=value;
}
if(keys.includes(key))
{
tmp[key]=value;
}
})
result.push(...tmp);
tmp = {};
});
You can do this with a map utilizing a couple of other array methods such as filter, and Object methods.
const keys = [
"_assembler",
"_css",
"_python",
"_php"
]
const source = [
{"handle":"frontend1", "_redis":"3", "_nodejs":"5", "_mysql":"2", "_python":"3", "_mongo":"4"},
{"handle":"frontend3", "_php":"4", "_mysql":"4", "_oracle":"4", "_ruby":"3", "_mongo":"5", "_python":"5"},
{"handle":"frontend4", "_java":"5", "_ruby":"5", "_mysql":"5", "_mongo":"5"}
];
const result = source.map( s => ({
handle: s.handle,
...Object.fromEntries(Object.entries(s).filter(x => x[0] != "handle" && keys.includes(x[0])))
}));
console.log(result);
Say I have an array of objects that looks like this
let myArray = [
{item1: true},
{item2: false},
{item3: true},
{item4: false}
]
How would I iterate though this to return a new array of true values that looks like this:
let newArray = ['item1', 'item3']
I found this function but it only returns single items:
function findKey(map, term) {
var found = [];
for(var property in map) {
if(map.hasOwnProperty(property)) {
for(var key in map[property]) {
if(map[property].hasOwnProperty(key) && key === term) {
found.push(property);
}
}
}
}
return found;
}
Assuming myArray always contains objects with only 1 property.
let newArray = myArray
.map(item => Object.entries(item)[0])
.filter(([key, value]) => value)
.map(([key, value]) => key)
You could access the first key of each array item via Object.keys(), and use this to filter items with a true value for that first key, and then complete the process with a call to map() to transform the item to a value based on the same "first key" technique:
let myArray = [
{item1: true},
{item2: false},
{item3: true},
{item4: false}
]
let result = myArray
.filter(item => item[ Object.keys(item)[0] ] === true)
.map(item => Object.keys(item)[0])
console.log(result)
Use the function reduce to build the desired output. The handler of the function reduce will get the keys and check for each value === true.
This approach checks for the whole set of keys within an object. Further, this way you only use one loop.
let myArray = [{item1: true},{item2: false},{item3: true},{item4: false}],
result = myArray.reduce((a, c) => a.concat(Object.keys(c).filter(k => c[k] === true)), []);
console.log(result);
Something much optimized than the accepted answer would look like this:
const arr = [
{ item1: true },
{ item2: false },
{ item3: true },
{ item4: false }
]
const result = [];
const len = arr.length;
for (let i = 0; i < len; ++i) {
const obj = arr[i];
const key = Object.keys(obj)[0];
if(obj[key]) {
result.push(key);
}
}
console.log(result);
There is only one loop over the array, instead of map and filter which ends up looping twice.
Shortest
let newArray = myArray.map( x=>Object.keys(x)[0] ).filter( (k,i)=>myArray[i][k] );
In above solution first we use: map which works as for-loop to get array of keys (using Object.keys) ["item1", "item2", "item3", "item4"]. Then we filter that array by choose only those keys for which original array object has true. e.g myArray[0]["item1"] -> true (we use fact that filter funtion takes array element (k) and its index (i) which is the same for elements in myArray). In map and filter we use arrow functions.
I've read this answer on SO to try and understand where I'm going wrong, but not quite getting there.
I have this function :
get() {
var result = {};
this.filters.forEach(filter => result[filter.name] = filter.value);
return result;
}
It turns this :
[
{ name: "Some", value: "20160608" }
]
To this :
{ Some: "20160608" }
And I thought, that is exactly what reduce is for, I have an array, and I want one single value at the end of it.
So I thought this :
this.filters.reduce((result, filter) => {
result[filter.name] = filter.value;
return result;
});
But that doesn't produce the correct result.
1) Can I use Reduce here?
2) Why does it not produce the correct result.
From my understanding, the first iteration the result would be an empty object of some description, but it is the array itself.
So how would you go about redefining that on the first iteration - these thoughts provoke the feeling that it isn't right in this situation!
Set initial value as object
this.filters = this.filters.reduce((result, filter) => {
result[filter.name] = filter.value;
return result;
},{});
//-^----------- here
var filters = [{
name: "Some",
value: "20160608"
}];
filters = filters.reduce((result, filter) => {
result[filter.name] = filter.value;
return result;
}, {});
console.log(filters);
var filters = [{
name: "Some",
value: "20160608"
}];
filters = filters.reduce((result, {name, value}= filter) => (result[name] = value, result), {});
console.log(filters);
Since 2019 (ES2019) you can go with Object.fromEntries() but you need to map to array first.
const filtersObject = Object.fromEntries(filters.map(({ name, value }) => [name, value])
I've an array of elements as follows
entities
[
{
"name":"tiger",
"imageurl":"https://someurl.com",
"type":"animal"
},
{
"name":"cat",
"imageurl":"https://someurl.com",
"type":"animal"
},
{
"name":"parrot",
"imageurl":"https://someurl.com",
"type":"bird"
},{
"name":"potato",
"imageurl":"https://someurl.com",
"type":"vegetable"
},
{
"name":"orange",
"imageurl":"https://someurl.com",
"type":"fruit"
},
{
"name":"orange",
"imageurl":"https://someurl.com",
"type":"colour"
}
]
I've another array which is as follows
elemToRemove
[orange#fruit,cat#animal,tiger#animal]
I want to remove the elements having name=orange and type=fruit, name=cat and type=animal, name=tiger and type=animal
It is easily possible to remove the element based on single property by using filter over the array but in this case I am not able to put up map/filter/reduce to remove these elements.
I used split to create a name and type array and tried to do this but as we've type repeating the condition always returned false.
let nameArray = elemToRemove.map(function (elem) {
return elem.split('#')[0];
});
let typeArray= elemToRemove.map(function (elem) {
return elem.split('#')[1];
});
var reqData= entities.filter(function (obj) {
return (nameArray.indexOf(obj.name) === -1 && typeArray.indexOf(obj['env']) === -1);
});
Thus always giving me an empty reqData array. I do not have a provision to have an id or else I could've used id to delete the elements.
Expected Output
[
{
"name":"parrot",
"imageurl":"https://someurl.com",
"type":"bird"
},{
"name":"potato",
"imageurl":"https://someurl.com",
"type":"vegetable"
},
{
"name":"orange",
"imageurl":"https://someurl.com",
"type":"colour"
}
]
What is most elegant way to achieve this?
Map tends to be useful for these sorts of problems with the bonus of sublinear value retrieval.
// Input.
const input = [{"name":"tiger","imageurl":"https://someurl.com","type":"animal"},{"name":"cat","imageurl":"https://someurl.com","type":"animal"},{"name":"parrot","imageurl":"https://someurl.com","type":"bird"},{"name":"potato","imageurl":"https://someurl.com","type":"vegetable"},{"name":"orange","imageurl":"https://someurl.com","type":"fruit"},{"name":"orange","imageurl":"https://someurl.com","type":"colour"}]
// Tags.
const tags = ["orange#fruit", "cat#animal", "tiger#animal"]
// Clean.
const clean = (array, tags) => {
const map = new Map(array.map(x => [`${x.name}#${x.type}`, x])) // Create Map.
tags.forEach(tag => map.delete(tag)) // Remove each tag from Map.
return Array.from(map.values()) // Return Array from Map.values().
}
// Output.
const output = clean(input, tags)
// Proof.
console.log(output)
You can use array.filter:
var arr = [
{"name":"tiger","imageurl":"https://someurl.com","type":"animal"},
{"name":"cat","imageurl":"https://someurl.com","type":"animal"},
{"name":"parrot","imageurl":"https://someurl.com","type":"bird"},
{"name":"potato","imageurl":"https://someurl.com","type":"vegetable"},
{"name":"orange","imageurl":"https://someurl.com","type":"fruit"},
{"name":"orange","imageurl":"https://someurl.com","type":"colour"}
];
var toRemove = ['orange#fruit', 'cat#animal', 'tiger#animal'];
var filterOut = toRemove.map(e => {
var [name, type] = e.split('#');
return {name, type};
});
arr = arr.filter(e => !filterOut.find(({name, type}) => e.name === name && e.type === type));
console.log(arr);
You can use filter() to select only those objects which doesn't match desired criteria. We will test each object using .some() to find if there any match found between object and the array having strings to check.
let data = [{"name":"tiger", "imageurl":"https://someurl.com", "type":"animal"}, {"name":"cat", "imageurl":"https://someurl.com", "type":"animal"}, {"name":"parrot", "imageurl":"https://someurl.com", "type":"bird"}, { "name":"potato", "imageurl":"https://someurl.com", "type":"vegetable"}, { "name":"orange", "imageurl":"https://someurl.com", "type":"fruit"}, { "name":"orange", "imageurl":"https://someurl.com", "type":"colour"}];
let arr = ['orange#fruit', 'cat#animal', 'tiger#animal'];
let result = data.filter(o => !arr.some(s => (
[name, type] = s.split('#'),
o['name'] === name && o['type'] === type
)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Docs:
Array.prototype.filter()
Array.prototype.some()
String.prototype.split()
If your definition of elegant is to have the least possible code (to avoid human error), and reutilize elements that have been already created by others, I recommend using an external library like Lodash that already have a function to do this.
The first part if a bit complex since I'm parting from having a string:
[orange#fruit,cat#animal,tiger#animal]
that needs to be parsed, instead of having already an array of values like the other answers.
// First we need to convert the filter to a proper Json representation.
// This is needed since the _.remove function takes a Json object.
// This could be simplified if your filter string were already a
// Json object.
var filter = "[orange#fruit,cat#animal,tiger#animal]";
filter = filter.replace(/(\w+)#(\w+)[,\]]/g, (m, p1, p2, offset, string) => {
return `{"name":"${p1}","type":"${p2}"}${m.includes(']')?']':','}`;
});
filter = JSON.parse(filter);
// Next, apply the filter to the remove function from Lodash.
// Once you have a Json object, it's only two lines of code.
const rm = _.partial(_.remove, obj);
filter.forEach(rm)
var obj = [
{
"name":"tiger",
"imageurl":"https://someurl.com",
"type":"animal"
},
{
"name":"cat",
"imageurl":"https://someurl.com",
"type":"animal"
},
{
"name":"parrot",
"imageurl":"https://someurl.com",
"type":"bird"
},{
"name":"potato",
"imageurl":"https://someurl.com",
"type":"vegetable"
},
{
"name":"orange",
"imageurl":"https://someurl.com",
"type":"fruit"
},
{
"name":"orange",
"imageurl":"https://someurl.com",
"type":"colour"
}
];
// First we need to convert the filter to a proper Json representation.
// This is needed since the _.remove function takes a Json object.
// This could be simplified if your filter string were already a
// Json object.
var filter = "[orange#fruit,cat#animal,tiger#animal]";
filter = filter.replace(/(\w+)#(\w+)[,\]]/g, (m, p1, p2, offset, string) => {
return `{"name":"${p1}","type":"${p2}"}${m.includes(']')?']':','}`;
});
filter = JSON.parse(filter);
// Next, apply the filter to the remove function from Lodash.
// Once you have a Json object, it's only two lines of code.
const rm = _.partial(_.remove, obj);
filter.forEach(rm)
console.log(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>