How apply ko.utils.arrayGetDistinctValues on two dimensional arrays - javascript

I have an ko.observableArray with two dimensions, i.e. "id" and "name".
Id will always be distinct, but name can hold the same value.
self.myArray = ko.observableArray( [
{ "id": "1", "name": "Barbara" }, 
{ "id": "2", "name": "Edwin" }, 
{ "id": "3", "name": "Barbara" } 
] );
However I want to filter the array so that my result holds only the first occurrence of "name".
The result I want is:
self. myFilteredArray = [
{ "id": "1", "name": "Barbara" }, 
{ "id": "2", "name": "Edwin" } 
];
I have been trying to figure this out using ko.utils.arrayGetDistinctValues(), but how can I utilize it on solely one chosen dimension?

You could use a computed for this:
viewModel.myFilteredArray = ko.computed(function() {
var rv = [], names = [];
for(var i=0; i<this.myArray().length; i++) {
if(names.indexOf(this.myArray()[i].name) === -1) {
rv.push(this.myArray()[i]);
names.push(this.myArray()[i].name);
}
}
return rv;
}, viewModel);

I can suggest a function, that loops through the array, builds map of property values for each item and checks, whether the current value was already added. Something like this:
function filterByFirstOccurance(arr, prop) {
var propValuesHash = {},
result = [];
for (var i = 0, l = arr.length; i < l; i++) {
var item = arr[i],
propValue = item[prop];
if (item.hasOwnProperty(prop) && !propValuesHash[propValue]) {
propValuesHash[propValue] = true;
result.push(item);
}
}
return result;
};
Fiddle

Related

Javascript Push to Array if Condition is Met

I have the following Foods Object:
var Foods = {
"Fruits": [{
"id": "1",
"Name": "Granny Smith",
"Category": "1"
}, {
"id": "2",
"Name": "Raspberries",
"Category": "1"
}
],
"Potatoes": [{
"id": "3",
"Name": "Maris Piper",
"Category": "2"
}, {
"id": "4",
"Name": "Charlotte",
"Category": "2"
}]
}
What I would like to do is only push the produce that matches an id passed by a link.
Get Foods
This is what I have tried so far:
function getCat (id){
result = [];
for(let item in Foods) {
if(Foods[item].id == id) {
data[item].foreach(v=>result.push("<div class='box'><h2>" +
data[key].Name + "<br></div>"));
}
}
}
display();
function display() {
alert(result);
}
So if a user hits the link (which has an id of 2), the result array should contain "Charlotte" and "Maris Piper" but I am just drawing a blank.
Any help appreciated.
Cheers
Youre quite close, however theres a slight problem:
for(let item in Foods) {
console.log(Foods[item]);
/*
[{
"id": "1",
"Name": "Granny Smith",
"Category": "1"
}, {
"id": "2",
"Name": "Raspberries",
"Category": "1"
}
]
*/
So youre iterating over the categories, which are arrays.
Foods[item].id
is undefined as its an array and not a product. So we need to iterate the array to, e.g.
var result=[];
Object.values(Foods).forEach(function(category){
category.forEach(function(product){
if(product.id===id){
result.push(product);
}
});
});
Run
But if youre doing this quite often, it might be easier to create one product array once:
var products = Object.values(Foods).reduce((arr,cat)=>arr.concat(cat),[]);
So you can simply filter this whenever someone clicks a button:
var result = products.filter(product=>product.id === id);
Run
You're somewhat on the right track, but what's data? Why are you not doing anything with result? And you should be looking at the Category property rather than ID.
This'll work:
function getCat(id) {
let result = [];
for (let item in Foods) {
if (Foods.hasOwnProperty(item)) {
Foods[item].forEach((food) => {
if (food.Category == id) {
result.push(food);
}
});
}
}
console.log(result);
}
First of all result array should be at global scope so that you can access it in another function, And in object you are having categories then each category has some data in array so after iterating over object, you need to iterate the items from array as well to get the value. Check the below code.
var result = [];
function getCat(id){
for(let item in Foods) {
var foodItem = Foods[item];
for(let i=0; i<foodItem.length; i++){
if(foodItem[i].id == id) {
result.push("<div class='box'><h2>" + foodItem[i].Name + "<br></div>"));
}
}
}
}
function display() {
alert(result);
}
display();
Iterator is wrong. You should do it like this:
function getCat(id){
result = [];
for(let item in Foods) {
Foods[item].forEach(function(each){
if(each.id == id) { // you cmpare to the wrong target
// do something
}
});
}
}

Compare two object arrays and combine missing objects

I have 2 object arrays. The 1st is an array of managers. The 2nd is an array of selected managers from the 1st array. The difference is I added a property selected: true. I need to now replace the the managers in the first array with selected managers. I am doing this with an AngularJS service I created. I'm sure there is much simpler solution so I'm open to suggestions. JavaScript, jQuery, lodash, LINQ.js are good.
I have a plunker and I have displayed the result I need. Notice the manager that does not have the selected:true property.
plunker
var app = angular.module("mainModule", []);
var MainController = function($scope, service) {
var eventUsers = [
{
"event_Users_ID":1009,"event_ID":11,"user_ID":"15e640c1-a481-4997-96a7-be2d7b3fcabb"
},{
"event_Users_ID":1010,"event_ID":11,"user_ID":"250a19be-e661-4c04-9a50-c84b0e7349b7"
},{
"event_Users_ID":1011,"event_ID":11,"user_ID":"4cada7f0-b961-422d-8cfe-4e96c1fc11dd"
},{
"event_Users_ID":1013,"event_ID":11,"user_ID":"a3125317-5deb-426d-bbb1-06d3bd4ebaa6"
}];
var managers = [
{
"id": "15e640c1-a481-4997-96a7-be2d7b3fcabb",
"fullName": "Kul Srivastva"
},{
"id": "250a19be-e661-4c04-9a50-c84b0e7349b7",
"fullName": "Todd Brothers"
}, {
"id": "4cada7f0-b961-422d-8cfe-4e96c1fc11dd",
"fullName": "Rudy Sanchez"
}, {
"id": "79823c6d-de52-4464-aa7e-a15949fb25fb",
"fullName": "Mike Piehota",
}, {
"id": "a3125317-5deb-426d-bbb1-06d3bd4ebaa6",
"fullName": "Nick Broadhurst"
}];
$scope.result = service.eventUserMatch(eventUsers, managers);
};
function service() {
var vm = this;
vm.eventUserMatch = function (eventUsers, managers) {
var arry = [];
arry = $.map(eventUsers, function (eventUser) {
var manager = $.grep(managers, function (user) {
return user.id === eventUser.user_ID;
})[0];
eventUser.id = manager.id;
eventUser.fullName = manager.fullName;
eventUser.selected = true;
return eventUser;
});
return arry;
};
}
app.controller("MainController", MainController);
app.service('service', service);
You can use Array#map.
// Get all the event user IDs in an array
var eventUserIds = eventUsers.map(e => e.user_ID);
// Iterate over managers
managers = managers.map(e => {
// If manager is present in the event users, `select` it
if (eventUserIds.indexOf(e.id) !== -1) {
e.selected = true;
}
return e;
});
var eventUsers = [{
"event_Users_ID": 1009,
"event_ID": 11,
"user_ID": "15e640c1-a481-4997-96a7-be2d7b3fcabb"
}, {
"event_Users_ID": 1010,
"event_ID": 11,
"user_ID": "250a19be-e661-4c04-9a50-c84b0e7349b7"
}, {
"event_Users_ID": 1011,
"event_ID": 11,
"user_ID": "4cada7f0-b961-422d-8cfe-4e96c1fc11dd"
}, {
"event_Users_ID": 1013,
"event_ID": 11,
"user_ID": "a3125317-5deb-426d-bbb1-06d3bd4ebaa6"
}];
var managers = [{
"id": "15e640c1-a481-4997-96a7-be2d7b3fcabb",
"fullName": "Kul Srivastva"
}, {
"id": "250a19be-e661-4c04-9a50-c84b0e7349b7",
"fullName": "Todd Brothers"
}, {
"id": "4cada7f0-b961-422d-8cfe-4e96c1fc11dd",
"fullName": "Rudy Sanchez"
}, {
"id": "79823c6d-de52-4464-aa7e-a15949fb25fb",
"fullName": "Mike Piehota",
}, {
"id": "a3125317-5deb-426d-bbb1-06d3bd4ebaa6",
"fullName": "Nick Broadhurst"
}];
var eventUserIds = eventUsers.map(e => e.user_ID);
managers = managers.map(e => {
if (eventUserIds.indexOf(e.id) !== -1) {
e.selected = true;
}
return e;
})
console.log(managers);
document.getElementById('result').innerHTML = JSON.stringify(managers, 0, 4);
<pre id="result"></pre>
would this work? Loop through the new array of managers, find the index using lodash of a matching manager object in the old manager array and replace it in the old manager array with the manager from the new manager array if found?
There's probably a more efficient way to write a solution to this but assuming I'm understanding your problem correctly I believe this should work? Can't test as I'm at work currently.
for(var i=0; i < SelectedManagersArray.length; i++){
var index = _.findIndex(OldManagersArray, {id: SelectedManagersArray[i].id, fullName: selectedManagersArray[i].fullName);
//I believe lodash returns a -1 if a matching index isn't found.
if(index !== -1){SelectedManagersArray[index] = OldManagersArray[i]}
}
Simple implementation:
for(var i = 0; i < eventUsers.length; i++) {
for(var j = 0; j < managers.length; j++) {
if(eventUsers[i].user_ID === managers[j].id) {
managers[j].selected = true;
}
}
}
As you said, I do think there may be an easier way to do this.
I advise you to pick a look to SugarJs which is a JavaScript library that extends native objects with so helpful methods.
In your case the doc on Arrays.
For me, it helps a lot dealing with a lot of native JavaScript Object (JSON).

Removing Duplicate object from array in jquery code not working

This is my array in jquery , which contains duplicate objects/elements :
[{
"name": "hello",
"label": "world"
}, {
"name": "abc",
"label": "xyz"
}, {
"name": "hello",
"label": "world"
}]
I am using the following piece of code to remove duplicate elements but it not working the duplicate elements are not removed.
var result = [];
$.each(subservices, function (i, e) {
if ($.inArray(e, result) == -1)
result.push(e);
});
alert(JSON.stringify(result));
Function $.inArray works fine for simple types (e.g. number or string), but for complex types it does not produce the correct result, because it tries to match by reference. Instead of using inArray in your loop you can search the array using function grep:
var subservices = [{
"name": "hello",
"label": "world"
}, {
"name": "abc",
"label": "xyz"
}, {
"name": "hello",
"label": "world"
}
];
var result = [];
$.each(subservices, function (i, e) {
var matchingItems = $.grep(result, function (item) {
return item.name === e.name && item.label === e.label;
});
if (matchingItems.length === 0){
result.push(e);
}
});
//displays result [{"name":"hello","label":"world"},{"name":"abc","label":"xyz"}]
alert(JSON.stringify(result));
Here is a working jsFiddle
You need to filter array by unique name/value. Here is some pure JS solution:
var data = [{
"name": "hello",
"label": "world"
}, {
"name": "abc",
"label": "xyz"
}, {
"name": "hello",
"label": "world"
}];
var result = data.filter(function(el, i, x) {
return x.some(function(obj, j) {
return obj.name === el.name && (x = j);
}) && i == x;
});
alert(JSON.stringify(result, null, 4));
This is because these two objects are distinct, even though all the attributes inside are the same. You can see this from:
console.log(result[0] === result[2]);
which results in false.
Instead, you need to iterate through your array based on a unique identifier, such as name & label as such:
for(var i = 0, i < results.length; i++) {
if (result[i].name === ... && result[i].label === ...) {
index = i;
break;
}
}
to check if your item is unique.

JSON Data Fuzzy merge

I have a JSON data like this
{
"array": {
"InvestmentsDeposits": {
"NAME": "Investments & Deposits",
"PARENT": [
{
"CONTENT_ID": "Promotions",
"DISPLAY_ORDER": 3,
"PATH": "/Promotions"
}
]
},
"InvestmentsDeposits$$$d": {
"NAME": "Deposits",
"PARENT": [
{
"CONTENT_ID": "NewPromotion",
"text" : "newtext"
}
]
}
}
}
I need to search for fuzzy data and merge. For example InvestmentsDeposits and InvestmentsDeposits$$$d need to be merged because it matches closely in name
Need to use javascript for this
For now I can make sure source data will always have $$$d at the end to merge with the target data without $$$d i.e., InvestmentDeposits.
My final merged content should be like this
{
"array": {
"InvestmentsDeposits": {
"NAME": "Deposits",
"PARENT": [
{
"CONTENT_ID": "NewPromotion",
"DISPLAY_ORDER": 3,
"PATH": "/Promotions"
"text": "newtext"
}
]
}
}
}
any help on this one?
What I have tried so far
var json0 = {
"InvestmentsDeposits": {
"NAME": "Investments & Deposits",
"PARENT": [
{
"CONTENT_ID": "Promotions",
"DISPLAY_ORDER": 3,
"PATH": "/Promotions"
}
]
}
};
var json1 =
{
"InvestmentsDeposits$$$d": {
"NAME": "Deposits",
"PARENT": [
{
"CONTENT_ID": "NewPromotion",
"text" : "newtext"
}
]
}
};
// Merge object2 into object1, recursively
$.extend( true, json0, json1 );
I am able to merge the data if i am able to split the InvestmentDeposits and InvestmentDeposits$$$d in to two distinct JSON objects but how to split and move the $$$d data in to another object? to make the jquery extend work
Use Object.keys() to find an object's keys and figure out what data to move over. You can compare the first key with the others to find matches, then remove the keys you just looked at until all of them are gone. Here's an example with a similar object.
var dat = {
"InvestmentsDeposits": {
"NAME": "Investments & Deposits",
"CONTENT_ID": "Promotions",
"DISPLAY_ORDER": 3,
"PATH": "/Promotions"
}, "InvestmentsDeposits$$$d": {
"NAME": "Deposits",
"CONTENT_ID": "NewPromotion",
"text" : "newtext"
},
"NotLikeTheOthers": {
"Um": "Yeah."
}
};
var result = {}; // This will be the merged object
var keys = Object.keys(dat); // Contains keys
while(keys.length) {
var i=1;
for(; i<keys.length; i++) { // Find matches
if(keys[0] == keys[i] + '$$$d') { // Match type 1
result[keys[i]] = dat[keys[i]]; // Copy orig
for(var j in dat[keys[0]]) { // Replace values
result[keys[i]][j] = dat[keys[0]][j];
}
keys.splice(i,1);
keys.shift();
i = 0;
break;
} else if(keys[i] == keys[0] + '$$$d') { // Reverse matched
result[keys[0]] = dat[keys[0]];
for(var j in dat[keys[i]]) {
result[keys[0]][j] = dat[keys[i]][j];
}
keys.splice(i,1);
keys.shift();
i = 0;
break;
}
}
if(i > 0) { // Didn't find a match
result[keys[0]] = dat[keys[0]];
keys.shift();
}
}
alert(JSON.stringify(result));
Note that Object.keys() requires IE9+.

How to convert from a JSON array to a JSON object

I have this JSON that I'm getting back from a web service call:
{
"name": "My Name",
"path": "my path",
"id": "44",
"type": "my type",
"classes": "my classes"
},
{
"name": "his Name",
"path": "his path",
"id": "76",
"type": "his type",
"classes": "his classes"
}
I then need to convert it to this format
{
"44" : { "name" : "My Name", "path" : "my path" },
"76" : { "name" : "his Name", "path" : "his path" }
}
My initial naive attempt was this:
var myData = [];
for (var i = 0; i < rawData.length; i++) {
myData.push({
rawData[i].id :
{
"path": rawData[i].path,
"name": rawData[i].name
}
});
which fails with syntax errors, so I eventually got to this:
var myData = [];
for (var i = 0; i < rawData.length; i++) {
myData.push(rawData[i].id,
{
"path": rawData[i].path,
"name": rawData[i].name
});
and it mostly works. My array is populated, but the problem is that my myData array doesn't have the "44", and "76" part of the object, just the { "name" : "", "path" : "" } part. I expect this is due to a lack of understanding on my part of how JSON and javscript objects work.
Your desired output isn't an array, so that's your starting point. The output you've said you want is an object, not an array.
You build your result by creating a blank object and then adding the objects to it using id as the key:
var myData = {};
rawData.forEach(function(entry) {
myData[entry.id] = {
name: entry.name,
path: entry.path
};
});
Or if you don't want to use forEach (it's ES5, but can be shimmed for older browsers), the old-fashioned way:
var myData = {};
var index, entry;
for (index = 0; index < rawData.length; ++index) {
entry = rawData[index];
myData[entry.id] = {
name: entry.name,
path: entry.path
};
}
Don't use Array.prototype.push(), use the square bracket notation and define your output as an object not an array.
var myData = {};
for (var i = 0; i < rawData.length; i++) {
myData[rawData[i].id] = {
"path": rawData[i].path,
"name": rawData[i].name
}
}
You need to convert your id to a string?
var myData = {};
for (var i = 0; i < rawData.length; i++) {
myData[String(rawData[i].id)] = {
"path": rawData[i].path,
"name": rawData[i].name
};
}
A variation on what other posters have written:
// Create a new empty object.
var out = {};
// Loop over your array of objects
// Add the each object id as a key to the output object, and the object as the value.
for (var i = 0, l = arr.length; i <l; i++) {
var obj = arr[i];
out[obj.id] = obj;
// Delete the properties from the newly added object you don't want.
delete obj.id;
delete obj.type;
delete obj.classes;
}

Categories

Resources