This is the current JSON file:
[{
"name": "Peter",
"age": 30,
"hair color": "brown"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}]
I want to remove the duplicate Steve individual from the list. How can I make a new JSON that checks if the object's name matches and remove any duplicates in JavaScript?
You must load the JSON data in to the program and parse that with JSON.parse, like this
var array = JSON.parse(content.toString())
To filter out the repeated names from the array of Objects, we use Array.prototype.filter function. You can store the names in an object, and next time when the same name appears we simply filter it out from the result.
var seenNames = {};
array = array.filter(function(currentObject) {
if (currentObject.name in seenNames) {
return false;
} else {
seenNames[currentObject.name] = true;
return true;
}
});
console.log(array);
# [ { name: 'Peter', age: 30, 'hair color': 'brown' },
# { name: 'Steve', age: 55, 'hair color': 'blonde' } ]
var data = [{
"name": "Peter",
"age": 30,
"hair color": "brown"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}]
data = this.data.filter((obj, pos, arr) => {
return arr.map(mapObj =>
mapObj.name).indexOf(obj.name) == pos;
});
console.log(data);
Using Underscore.js and the uniq function:
_.uniq(array, false, function (item) { return item.name; })
Loop, check, splice, repeat:
var distinctValues = {};
for (var i = 0; i < data.length; i++) {
if (distinctValues.hasOwnProperty(data[i].name]) {
//already has it
data.splice(i, 1);
i--;
} else {
distinctValues[data[i].name] = true;
}
}
This was the best solution I could find that makes you able to filter on multiple values in you json object. Solution without _ (module)
array.filter((thing, index, self) =>
index === self.findIndex((t) => (
t.place === thing.place && t.name === thing.name // you can add more arguments here to filter more
))
)
//Other example
array.filter((thing, index, self) =>
index === self.findIndex((t) => (
t.place === thing.place && t.name === thing.name && t.time === thing.time
))
)
Python one-liner from CLI:
cat file_with_duplicates.json | python2 -c 'import sys; import json; sys.stdout.write(json.dumps(reduce(lambda x, y: x + [y] if y not in x else x, json.loads(sys.stdin.read()), [])))' > unique_items.txt
Related
Hi I have array of objects and I am trying to loop through them and have similar key values in new object here is example of data that I have.
let newDats = [{"ID":1, "Name": "Ahmed", "Age":17, "Score":84, "Absentee":3},
{"ID":2, "Name": "Hassan", "Age":15, "Score":87, "Absentee":2},
{"ID":3, "Name": "Aisha", "Age":18, "Score":86, "Absentee":2}]
And so on. However, what I want is something as:
data = [{ID:[1,2,3], Names:["Ahmed", "Hassan","Aisha"],
Ages:[17,15,18]}]
And so forth. My end goal is to perform basic descriptive statistics on the numeric parts of the data so if there is better ideas I would love to hear I am knida newbie to js .
PS
Column names (object keys ) can be more than this and unknown so I want something dynamic.
Thanks in advance.
You can use the function below. Just pass the array you want to sort.
function dataSorter(dataArr) {
const data = {};
for (const item in dataArr[0]) data[item] = [];
for (let i = 0; i < dataArr.length; i++) {
for (const item in dataArr[i]) {
data[item].push(dataArr[i][item]);
}
}
return data;
}
/* Example Below */
let newDats = [{
ID: 1,
Name: "Ahmed",
Age: 17,
Score: 84,
Absentee: 3
},
{
ID: 2,
Name: "Hassan",
Age: 15,
Score: 87,
Absentee: 2
},
{
ID: 3,
Name: "Aisha",
Age: 18,
Score: 86,
Absentee: 2
},
];
console.log(dataSorter(newDats));
.as-console-wrapper { max-height: 100% !important; top: 0; } /* Ignore this */
Something like:
let newDats = [{"ID":1, "Name": "Ahmed", "Age":17, "Score":84, "Absentee":3},
{"ID":2, "Name": "Hassan", "Age":15, "Score":87, "Absentee":2},
{"ID":3, "Name": "Aisha", "Age":18, "Score":86, "Absentee":2}];
data = {};
keys = [];
for(let keyVal in newDats[0]) {
keys.push(keyVal)
data[keyVal] = [];
}
newDats.forEach(dt => {
keys.forEach( kv => {
data[kv].push(dt[kv])
})
})
console.log(data)
You can simply reduce() the array of objects, iterating over all Object.keys() of each element and pushing them to the property array of the accumulator. This allows for each object having different keys.
let newDats = [{ "ID": 1, "Name": "Ahmed", "Age": 17, "Score": 84, "Absentee": 3 }, { "ID": 2, "Name": "Hassan", "Age": 15, "Score": 87, "Absentee": 2 }, { "ID": 3, "Name": "Aisha", "Age": 18, "Score": 86, "Absentee": 2 }];
const result = newDats.reduce((acc, o) => (
Object.keys(o).forEach(k => (acc[k] ??= []).push(o[k])), acc), {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Note the use of the logical nullish assignment operator (??=) which can be replaced by a logical OR short circuit for compatibility.
//acc[k] ??= []
acc[k] = acc[k] || []
I am trying to categorise the objects by comparing two objects say data and categories
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category":["A","1a", "2a"],
"B_category":["1b", "2b"],
"C_category":["1c", "2c"],
"D_category":["1d", "2d"]
};
I want to group the data based on the category object, when there is no match the group should be others and the resultant data should be like
const resultData = [
{ group: 'Others', name: '777', count: 456 },
{ group: 'A_category', name: '1a', count: 154 },
{ group: 'B_category', name: '1b', count: 765 },
{ group: 'C_category', name: '1c', count: 7877 }
]
I used the function but not able to achieve the result
const resultData = [];
function restructure(data, categories) {
Object.keys(data).map(
dataKey => {
for (let [key, value] of Object.entries(categories)) {
value.includes(dataKey) ? resultData.push({"group": key,...data[dataKey]}) : resultData.push({"group": "Others",...data[dataKey]}) ;
break;
}
}
)
}
restructure(data,categories);
You can try this as well. Iterate over your data entries and find whether the key exists in any of the categories object data and push it into the array with found category as group or push it with Others as group as shown in the below code
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category": ["A", "1a", "2a"],
"B_category": ["1b", "2b"],
"C_category": ["1c", "2c"],
"D_category": ["1d", "2d"]
};
const resultData = [];
Object.entries(data).map(([key, val])=>{
let group = Object.keys(categories).find(category=>categories[category].includes(key)) || 'Others'
resultData.push({
group,
...val
})
})
console.log(resultData)
Instead of for loop you need to use filter as let category = Object.entries(categories).filter(([key, value]) => value.includes(dataKey));.
If category.length > 0 then category is available else use Others.
Try it below.
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category": ["A", "1a", "2a"],
"B_category": ["1b", "2b"],
"C_category": ["1c", "2c"],
"D_category": ["1d", "2d"]
};
const resultData = [];
function restructure(data, categories) {
Object.keys(data).map(
dataKey => {
let category = Object.entries(categories)
.filter(([key, value]) => value.includes(dataKey));
resultData.push({
"group": category.length > 0 ? category[0][0] : "Others",
...data[dataKey]
});
})
}
restructure(data, categories);
console.log(resultData);
That's because you're breaking out of the loop regardless of whether you found the category or not. Your for loop will only execute once then breaks immediately. If the first category object matches, it is used, if not "Others" is assigned and the loop exits without checking the rest of the categories. Only break out of the loop if the lookup is successful:
for (let [key, value] of Object.entries(categories)) {
if(value.includes(dataKey)) { // if this is the category
resultData.push({ "group": key, ...data[dataKey] }); // use it ...
return; // ... and break the loop and the current iteration of forEach. The current object is handled
}
}
resultData.push({ "group": "Others", ...data[dataKey] }); // if the return statement above is never reached, that means the category was not found, assign "Others"
BTW, you can use other array methods to shorten things out like so:
function restructure(data, categories) {
return Object.keys(data).map(key => ({
"group": Object.keys(categories).find(cat => categories[cat].includes(key)) || "Others",
...data[key]
}));
}
Then use like so:
const resultData = restructure(data, categories);
My method uses find to try to find a category key that contains the name of the object, if find fails, it returns null at which point, the || "Others" part is evaluated and "Others" will be used as the group name (Does JavaScript have "Short-circuit" evaluation?).
Demo:
const data = {"777":{"name":"777","count":456},"1a":{"name":"1a","count":154},"1b":{"name":"1b","count":765},"1c":{"name":"1c","count":7877}};
const categories = {"A_category":["A","1a","2a"],"B_category":["1b","2b"],"C_category":["1c","2c"],"D_category":["1d","2d"]};
function restructure(data, categories) {
return Object.keys(data).map(key => ({
"group": Object.keys(categories).find(cat => categories[cat].includes(key)) || "Others",
...data[key]
}));
}
const resultData = restructure(data, categories);
console.log(resultData);
I'm facing a problem with filter method. On my page there's an input to search matches by team names. Filter value is being stored to React state. Matches object looks like this:
[
{
"id": 4,
"teamBlue": {
"id": 36,
"name": "nameForTeamBlue",
"playerList": [
{
[...]
}
]
},
"teamRed": {
"id": 37,
"name": "nameForTeamRed",
"playerList": [
{
[...]
}
]
},
"localDate": "2020-01-01",
"localTime": "00:00:00",
"referee": null,
"commentator1": null,
"commentator2": null,
"streamer": null,
"stage": {
"id": 2,
"name": "GROUPSTAGE"
},
"onLive": true,
"finished": false
},
]
I tried tons of methods to filter matches by team name, for example:
let criteria = {
teamBlue: {
name: this.state.filter
},
teamRed: {
name: this.state.filter
}
};
let filteredMatches = this.state.matches.filter(function(item) {
for (let key in criteria) {
if (item[key] === undefined || item[key] !== criteria[key])
return false;
}
return true;
});
console.log(filteredMatches);
but none of them worked.
Is there any way to filter these matches so when I type "blue" into my input, it will show all matches where team name contains "blue"?
Thanks in advance!
Try updating the condition to:
if (!item[key] || item[key].name !== criteria[key].name)
let filteredMatches = this.state.matches.filter(function(item) {
let flag = true;
for (let key in criteria) {
// update this to
if (!item[key] || item[key].name !== criteria[key].name)
flag = false;
}
return flag;
});
The name property is missing :
if (key in item && item[key].name !== criteria[key].name)
You're comparing objects with === which will return false. You either need to use a deep comparison method from a library, or implement it yourself like below:
const matches = [ {"id": 4,
"teamBlue": {
"id": 36,
"name": "nameForTeamBlue",
"playerList": []
},
"teamRed": {
"id": 37,
"name": "nameForTeamRed",
"playerList": []
},
}, {"id": 4,
"teamBlue": {
"id": 36,
"name": "nameForTeamBlue",
"playerList": []
},
"teamRed": {
"id": 37,
"name": "nameForTeamRead",
"playerList": []
},
}]
const criteria = {
teamBlue: {
name: 'nameForTeamBlue',
},
teamRed: {
name: 'nameForTeamRed',
}
}
const filteredMatches = matches.filter((item) => {
const allCriteriaMatched = Object.entries(criteria)
.every(([key, value]) => {
const matched = Object.entries(value).every(([criteriaKey, criteriaValue]) => {
const itemValue = item[key][criteriaKey]
const matched = itemValue == criteriaValue
if (!matched) console.log('Item %s does not matched criteria %s. Item\'s value is %s, but criteria value is %s', item[key]['id'], criteriaKey, itemValue, criteriaValue, criteriaValue)
return matched
})
if (!matched) return false
return true
}, {})
return allCriteriaMatched
})
console.log(filteredMatches);
Basically, you just need to go 1 level deeper :D if your criteria can have multiple nested objects, then there's no point doing it manually. You can try to map criteria to run against matches so that you don't use === on objects, but only primitives.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I have a json file like this
let myfile = [
{"name":"John","eid":664,"socialid":399,"testid":799},
{"name":"Sam","testid":249,"eid":64,"socialid":80},
{"name":"Albert","eid":422,"testid":20,"socialid":10},
{"name":"Michel","eid":497,"testid":15,"socialid":60}]
from this above json I want to filter all values by it's key name and push it to an array.
expected output is:
"name": ["John", "Sam", "Albert", "Michel"],
"eid": [664, 64, 422, 497],
"testid": [799, 249, 20, 15],
"socialid": [399, 80, 10, 60]
How to make this happen ?
I tried like this
let arr3 = [];
$.each( myfile, function( key, value ) {
if(this.hasOwnProperty('name'))
{
console.log("is there")
arr3.push(value);
}
});
console.log(arr3);
it's not working as expected.
You can use array#reduce to accumulate value corresponding to each key in an object.
let myfile = [{"name":"John","eid":664,"socialid":399,"testid":799}, {"name":"Sam","testid":249,"eid":64,"socialid":80}, {"name":"Albert","eid":422,"testid":20,"socialid":10}, {"name":"Michel","eid":497,"testid":15,"socialid":60}],
result = myfile.reduce((r,o) => {
Object.entries(o).forEach(([key,value]) => {
r[key] = r[key] || [];
r[key].push(value);
});
return r;
},{});
console.log(result);
You can reduce the array into one object:
let myfile = [
{ name: 'John', eid: 664, socialid: 399, testid: 799 },
{ name: 'Sam', testid: 249, eid: 64, socialid: 80 },
{ name: 'Albert', eid: 422, testid: 20, socialid: 10 },
{ name: 'Michel', eid: 497, testid: 15, socialid: 60 },
];
console.log(
myfile.reduce(
(result, item) =>
Object.entries(item).reduce((result, [key, value]) => {
result[key] = result[key] || [];
result[key].push(value);
return result;
}, result),
{},
),
);
You main problem is, you try to access publication which does not exist in the inner objects.
For an inline renaming, you could take an object with the given name as key and the new name as value and take the actual key as default value. Then create a new array, if not exists and push the value to it.
Finally, you get an object with wanted keys with all value from the given data.
var myfile = [{ "name": "John", "eid": 664, "socialid": 399, "testid": 799 }, { "name": "Sam", "testid": 249, "eid": 64, "socialid": 80 }, { "name": "Albert", "eid": 422, "testid": 20, "socialid": 10 }, { "name": "Michel", "eid": 497, "testid": 15, "socialid": 60 }],
replace = { name: 'publication' },
result = myfile.reduce((r, o) => Object.entries(o).reduce((p, [k, v]) => {
k = replace[k] || k;
(p[k] = p[k] || []).push(v);
return p;
}, r), Object.create(null));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Like this?
let json=[
{"name":"John","eid":664,"socialid":399,"testid":799},
{"name":"Sam","testid":249,"eid":64,"socialid":80},
{"name":"Albert","eid":422,"testid":20,"socialid":10},
{"name":"Michel","eid":497,"testid":15,"socialid":60}
]
let output={
publication:[],
eid:[],
testid:[],
socialid:[]
}
for(let i in json){
output.publication.push(json[i].name)
output.eid.push(json[i].eid)
output.testid.push(json[i].testid)
output.socialid.push(json[i].socialid)
}
console.log(output)
I hope that this will help you!
I have an array of objects like this:
let array = [{
"Age": 20,
"Name": "Kevin"
}, {
"Age": 15,
"Name": "Alfred"
}, {
"Age": 30,
"Name": "Joe"
}];
I want to get an object like this:
{
"Age": '20, 15, 30',
"Name": 'Kevin, Alfred, Joe'
}
If I do:
let r = array.reduce(function(pV, cV) {
Object.keys(cV).map(function(key){
pV[key] = (pV[key] || []).concat(cV[key]);
});
return pV;
},{});
console.log(r); // { "Age": [20, 15, 30], "Name": ['Kevin', 'Alfred', 'Joe'] }
Or if I do:
let r = array.reduce(function(pV, cV) {
Object.keys(cV).map(function(key){
pV[key] = (pV[key] || '') + ', ' + cV[key];
});
return pV;
},{});
console.log(r); // { "Age": ', 20, 15, 30', "Name": ', Kevin, Alfred, Joe' }
I'm a little bit lost. Some ideas on how to get what I need?
You are quite close with your second appraoch, you just have to make sure that the initial , does not appear, which is quite easy with a ternary:
let r = array.reduce(function(pV, cV) {
Object.keys(cV).map(function(key){
pV[key] = (pV[key] ? (pV[key] + ", ") : '') + cV[key];
});
return pV;
},{});
You can try following code using .reduce() and Object.keys
let array = [{
"Age": 20,
"Name": "Kevin"
}, {
"Age": 15,
"Name": "Alfred"
}, {
"Age": 30,
"Name": "Joe"
}];
let result = array.reduce((current,result) => {
Object.keys(current).forEach(key => {
if(!result[key]){
result[key] = current[key];
} else {
result[key]+= ", " + current[key];
}
})
return result;
},{});
console.log(result);
I wouldn't use reduce like that, which is just a glorified loop:
let r = {};
for (const cV of array) {
for (const key in cV) {
r[key] = (r[key] || []).concat(cV[key]);
}
}
For a functional approach, where the map would be actually useful, I'd nest the iterations the other way round:
let r = {};
for (const key of ["Age", "Name"]) { // or Object.keys(array[0])
r[key] = array.map(function(cV){
return cV[key];
}).join(", ");
}
You can do in this way as well.
array.reduce((a,n)=>{
a.Age = a.Age+','+n.Age;
a.Name = a.Name+','+n.Name;
return a;
});
Warning!! this approach with modify actual array of object.
If that is not intended then you can clone object first and then do reduce.
I did JSON.parse(JSON.stringify(array)) to clone array you can use your own ways to deep clone it.
JSON.parse(JSON.stringify(array)).reduce((a,n)=>{
a.Age = a.Age+', '+n.Age;
a.Name = a.Name+', '+n.Name;
return a;
})