Comparing information from an API call and an Array - javascript

I'm trying to compare the results of an API call to an existing array. Basically, I want to make a function that will loop through the array, then loop through the data from the API to see if there's a match.
Here's an example of the array I'm working with
let array = [ {
"name": "student1",
"id": 134},
{
"name": "student2",
"id": 135},
{
"name": "student3",
"id": 136}
]
Here's my function in JavaScript/jQuery
function getData() {
$.ajax({
url: "www.studentapi.com",
dataType: "json"
}).done(function(data) {
console.log(data)
}
}
The data I get back looks kind of like this:
[ {
"id": 134,
"score": 45},
{
"id": 138,
"score": 67},
{
"id": 139,
"score": 34}
]
I'm trying to find a way to find the matching ids in the array and in the data. So far I've tried:
for (let j =0; j < data.length; j++) {
if (array[j]["id"] === data[j].id) {
console.log("we have a match!")
}
else {
console.log("not a match!");
}
}
But this isn't working. Am I doing something incorrectly over here?

You can use find on an array to find an element that matches some conditional.
The below logic also uses arrow functions, but could be changed to use normal function(){}
let array = [
{
"name": "student1",
"id": 134
},
{
"name": "student2",
"id": 135
},
{
"name": "student3",
"id": 136
}
];
let data = [
{
"id": 134,
"score": 45
},
{
"id": 138,
"score": 67
},
{
"id": 139,
"score": 34
}
];
let studentData = array.map(student=>{
student.data = data.find(record=>record.id === student.id) || {};
return student;
});
console.log(studentData);

I would use the javascript filter function.
let matchingStudents = array.filter(student => {
return data.find(jsonData => student.id === jsonData.id);
});
There matchingStudents would hold all students present in the first array that are present in the second.
If you are wondering about the syntax, this is ES6. Next generation javascript. To write it in old javascript it'd be:
var matchingStudents = array.filter(function(student){
return data.find(function(jsonData){ return student.id === jsonData.id});
}

To specifically answer your question Am I doing something incorrectly over here?
Your search code here assumes that array and data will contain the exact same ids in the exact same order:
for (let j =0; j < data.length; j++) {
if (array[j]["id"] === data[j].id) {
Based on the sample data you provided, this isn't the case; you can't always compare array[j] to data[j] to match ids because (for example) it's possible you need to match array[4] to data[6].
One solution to this problem is to use a nested loop:
for (let i = 0; i < array.length; i++) {
for (let j = 0; j < data.length; j++) {
if (array[i].id === data[j].id) {
This way you'll compare every entry in array to every entry in data when looking for matches. (This is similar to what the solutions suggesting array.map and data.find are doing, with some smart early-out behavior.)
Another approach would be to sort both lists and step forward through them together.
let array = [
{ "id": 134, "name": "student1" },
{ "id": 139, "name": "student2" },
{ "id": 136, "name": "student3" }
];
let data = [
{ "id": 134, "score": 45 },
{ "id": 138, "score": 67 },
{ "id": 139, "score": 34 }
];
array.sort((a, b) => a.id - b.id)
data.sort((a, b) => a.id - b.id)
let data_i = 0;
for (let array_i = 0; array_i < array.length; array_i++) {
while (data[data_i].id < array[array_i].id) {
data_i++;
}
if (data_i < data.length && data[data_i].id === array[array_i].id) {
console.log(`Matched ${array[array_i].name} to score ${data[data_i].score}`);
} else {
console.log(`No match found for ${array[array_i].name}`);
}
}

Related

Repeat every element in array based on object properties

I have an array that I'm retrieving from an API. The array looks like this:
[{
"name": "Rachel",
"count": 4,
"fon": "46-104104",
"id": 2
},
{
"name": "Lindsay",
"count": 2,
"fon": "43-053201",
"id": 3
},
{
"name": "Michael",
"count": 5,
"fon": "46-231223",
"id": 4
}]
Then I loop through the array to create an array containing only the names.
function buildName(data) {
for (var i = 0; i < data.length; i++) {
nameList.push(data[i].name)
}
}
This also works so far, but I would like to create an array in which each name occurs as often as the object count says.
For example, the name Michael should appear five times in the array and Lindsay twice.
[
"Rachel",
"Rachel",
"Rachel",
"Rachel",
"Lindsay",
"Lindsay",
"Michael",
"Michael",
"Michael",
"Michael"
"Michael"
]
For each object create a new array using count, and then fill it with the name.
If you use flatMap to iterate over the array of objects. It will return a new array of nested objects but then flatten them into a non-nested structure.
const data=[{name:"Rachel",count:4,fon:"46-104104",id:2},{name:"Lindsay",count:2,fon:"43-053201",id:3},{name:"Michael",count:5,fon:"46-231223",id:4}];
const out = data.flatMap(obj => {
return new Array(obj.count).fill(obj.name)
});
console.log(out);
I've upgraded your functions but you can use the map method
function buildName(data){
for (let i = 0; i < data.length; i++){
let numToLoop = data[i].count
let name = data[i].name
for (let z = 0; z < +numToLoop; z++){
nameList.push(name)
}
}
}
Use an inner while loop inside the for loop:
const data = [{
"name": "Rachel",
"count": 4,
"fon": "46-104104",
"id": 2
},
{
"name": "Lindsay",
"count": 2,
"fon": "43-053201",
"id": 3
},
{
"name": "Michael",
"count": 5,
"fon": "46-231223",
"id": 4
}]
function buildName(data){
const result = [];
for (let i = 0; i < data.length; i += 1) {
let item = data[i];
let count = item.count;
while (count > 0) {
result.push(item.name);
count -= 1;
}
}
return result;
}
console.log(buildName(data));
Just add an inner loop with as many iterations as the "count" property in the object:
function buildName(data) {
const nameList = [];
for (var i = 0; i < data.length; i++) {
for (let j = 0; j < data[i].count; j++) {
nameList.push(data[i].name);
}
}
return nameList;
}
For fun
import { pipe } from 'fp-ts/lib/function';
import { chain, replicate } from 'fp-ts/lib/Array';
const arr = ...
const result = pipe(
arr,
chain(i => replicate(i.count, i.name))
);
You can use .flapMap() for that:
const arr = [{ "name": "Rachel", "count": 4, "fon": "46-104104", "id": 2 }, { "name": "Lindsay", "count": 2, "fon": "43-053201", "id": 3 }, { "name": "Michael", "count": 5, "fon": "46-231223", "id": 4 }];
const result = arr.flatMap(({count, name}) => Array(count).fill(name));
console.log(result);
Effectively you turn every element into an array of the the name property repeated count times which is then flattened into a single array.
It can be done via creating an array with repeated names in this way:
Array(count).fill(name)
Then you have to spread it into resulting array.
You can try this one-liner
const getNames = (data) =>
data.reduce(
(names, { name, count }) => [...names, ...Array(count).fill(name)],
[]
)
Note that a pure function is presented here, which is generally the preferred way of writing code. However, updating your example code might look like this
const getNames = (data) =>
data.reduce(
(names, { name, count }) => [...names, ...Array(count).fill(name)],
[]
)
function buildName(data) {
nameList = getNames(data)
}

complicated custom filter for nested ng-repeat

I've stuck for this for 2 days, tried so many ways still couldn't get it right. I can't change the API data so I have to deal with front end handling. I really need some help.
$scope.stationary = [{
"name": "Pen",
"data": [{
"date": "1-10-2017",
"inventory": 25
}, {
"date": "2-10-2017",
"inventory": 21
}]
}, {
"name": "Color Pencil",
"data": [{
"date": "1-10-2017",
"inventory": 3
}, {
"date": "2-10-2017",
"inventory": 0
}]
}, {
"name": "Color Pencil Special",
"data": [{
"date": "1-10-2017",
"inventory": 2
}, {
"date": "2-10-2017",
"inventory": 1 // make this view to '-' since inventory of color pencil is zero
}]
}]
http://jsfiddle.net/op2zd2vr/
The case is if color pencil's inventory is zero, should display '-' on color pencil special column.
Try this:
Updated Answer:
myApp.filter('customFilter', function() {
return function(items) {
for (var i = 0; i < items.length; i++) {
for (var k = 0; k < items[i].data.length; k++) {
if (i != 0 && items[i - 1].data[k].inventory == 0) {
items[i].data[k]['isZero'] = true;
} else {
items[i].data[k]['isZero'] = false;
}
}
}
return items;
};
});
See Updated jsfiddle link.
It's working for me.
Guess you have to manipulate the datas itselves, you cannot simply use a filter for this particular case.
Here's my fiddle: http://jsfiddle.net/op2zd2vr/2/
Solution used (depending ont the datas' names):
var positions = [];
$scope.stationary.forEach(function (s) {
if(s.name === 'Color Pencil'){
for(var i = 0; i < s.data.length; i++){
if(s.data[i].inventory === 0){
positions.push(i);
}
}
}
if(s.name === 'Color Pencil Special'){
for(var i = 0; i < s.data.length; i++){
if(positions.indexOf(i) >= 0){
s.data[i].inventory = '-';
}
}
}
});

Merging linked Data in Array in Javascript

I have a simple task of rearranging a couple of Arrays in a JSON, so ractive.js can handle it better. But I got carried away a bit, and the outcome was not particularly satisfactory.
An example of my initial Array:
[{
"_id": 1,
"type": "person",
"Name": "Hans",
"WorksFor": ["3", "4"],
}, {
"_id": 2,
"type": "person",
"Name": "Michael",
"WorksFor": ["3"],
}, {
"_id": 3,
"type": "department",
"Name": "Marketing"
}, {
"_id": 4,
"type": "department",
"Name": "Sales"
}, {
"_id": 5,
"type": "person",
"Name": "Chris",
"WorksFor": [],
}]
So with a given Department I wanted a method in ractive to give me all Persons who work in this Department (with a list of Departments they work for). Something like:
[{
"_id": 1,
"type": "person",
"Name": "Hans",
"WorksFor": ["3", "4"],
"Readable": ["Marketing", "Sales"]
}, {
"_id": 2,
"type": "person",
"Name": "Michael",
"WorksFor": ["3"],
"Readable": ["Sales"]
}]
The function that somehow came to life was similar to this:
function imsorryforthis() {
let output = [];
let tempdocs = this.get('docs'); //as this happens in a ractive method,
//"this.get" is neccesary for binding
for (var i = 0; i < tempdocs.length; i++) {
if (_.contains(tempdocs[i].WorksFor, givenDepartment)) { //I used underscore.js here
let collectedDepartmentData = [];
if (tempdocs[i].WorksFor.length > 0) {
for (var f = 0; f < tempdocs[i].WorksFor.length; f++) {
for (var g = 0; g < tempdocs.length; g++) {
if (tempdocs[i].WorksFor[f] == tempdocs[g]._id) {
let actualDepartmentData = {};
actualDepartmentData = tempdocs[g];
collectedDepartmentData.push(actualDepartmentData);
}
}
}
}
tempdocs[i].Readable = collectedDepartmentData;
output.push(tempdocs[i]);
}
}
return output;
}
I've put it in a Fiddle as well to make it better readable.
Due to the fact that somehow this monstrosity does work (I was astonished myself), it feels like scratching your left ear with your right hand over your head (while being constantly shouted at by a group of desperate mathematicians).
Maybe anybody knows a more presentable and smarter approach (or a way to compile JavaScript so this never sees the light of day again).
Construct a map department_id => department_name first:
let departments = {};
for (let x of data) {
if (x.type === 'department') {
departments[x._id] = x.Name;
}
}
Then, iterate over Persons and populate Readable arrays from that map:
for (let x of data) {
if (x.type === 'person') {
x.Readable = x.WorksFor.map(w => departments[w]);
}
}
Finally, extract Persons for the specific Department:
personsInSales = data.filter(x =>
x.type === 'person' && x.WorksFor.includes('3'));
Firstly, your data structure does not have a good design. You should not be returning person and department in the same array. If possible, try to redesign the initial data structure to make it more modular, by separating out the people and department into separate structures. However if you are stuck with this same data structure, you can write the code a little better. Please find the code below. Hope it helps!
function mapPeopleDepartment() {
var deptMap = {},peopleList = [];
//Iterate through the initialArray and separate out the department into a hashmap deptMap and people into a new peopleList
for(var i=0; i < initArray.length; i++) {
var obj = initArray[i];
obj.type == "department" ? deptMap[obj._id] = obj.Name : peopleList.push(obj);
}
//Iterate through the peopleList to map the WorksFor array to a Readable array
for(var i=0; i < peopleList.length; i++) {
var person = peopleList[i];
person.Readable = _.map(person.WorksFor, function(dept){return deptMap[dept]});
}
return peopleList;
}

theArray is not defined

I try to avoid to use $.grep to filter what needs to be filter. In my case I want to remove array of objects which has an id of '123', but I'm getting theArray is not defined error.
function filter(array, conditionFunction) {
var validValues = [];
for (var index = 0; index < array.length; i++) {
if (conditionFunction(theArray[index])) {
validValues.push(theArray[index]);
}
}
}
var cart = [
{
"id": "123456",
"name": "banana",
"image": "56fca57eb239dc38e355c86b-1459398061689-2013-Le-Tour-de-Langkawi-Stage-5-3.jpg",
"price": 12,
"discount_price": 8,
"qty": 4
},
{
"id": "123",
"name": "Christ come",
"image": "56fcb471b239dc38e355c86c-1459401869191-klcc.jpg",
"price": 12.9,
"discount_price": 11.9,
"qty": 4
}
]
cart = filter(cart, function(e) {
return e.id !== '123';
});
console.log(cart);
This ought to fix your problem. You changed your array variable to be theArray in the middle of your for-loop. Javascript can't know that, so you'll have to be more supportive and tell JS which array you mean.
for (var index = 0; index < array.length; i++) {
if (conditionFunction(array[index])) {
validValues.push(array[index]);
}
}

complex grouping and merging array as properties

I wish to put an array into other's array as proproties by matching their common properties. I want jobDetails's uId to match with job's uId. Possible?
var job = [{
"uId": 1
}, {
"uId": 2
}]
var jobDetails = [{
"uId": 1,
"salary": 5000
}, {
"uId": 2,
"salary": 5000
}]
is it possible to produce something like
var job = [{
"uId": 1,
"salary": [{
"uId": 1,
"salary": 5000
}]
}, {
"uId": 2,
"salary": [{
"uId": 2,
"salary": 5000
}]
}];
You may try something like this: http://jqversion.com/#!/XWFtbQb
for (var i = 0; i < job.length; i++) {
for (var j = 0; j < jobDetails.length; j++) {
if (job[i].uId == jobDetails[j].uId) {
job[i].salary = jobDetails[j];
}
}
}
console.log(job);
This is not a pure javascript solution, but I like to use underscore.js for this kind of typical actions on collections:
http://jsfiddle.net/FPwq7/
var finalCollection = [];
_.each(job, function(model){
var obj = _.findWhere(jobDetails, {uId: model.uId});
_.extend(model, {'salaray': obj});
finalCollection.push(model);
});
console.log(finalCollection);
I found that this Javascript utility belt takes care of some heavy lifting, and it makes the code a bit more pleasant to read than reading dry loops.
Yes possible , you need to play with both json objects
var array = [];
var object = {}
$.each( job, function ( k , kal ) {
$.each( jobDetails , function ( i , val) {
object.uId = i;
object.salary = val;
});
array.push(object);
});
console.log(JSON.stringify(array));

Categories