0

Im currently having some problems getting a distinct list of values from an array.

what i am looking for is something that will give me a count of distict values in the form

I have the following Array of Items

[{"Office":"abc", "Name":"ABC", "Total":0},
{"Office":"def", "Name":"DEF", "Total":11},
{"Office":"def", "Name":"DEF", "Total":1},
{"Office":"ghi", "Name":"GHI", "Total":1111}]

and i am looking for the following output which is a distinct list of Offices with the number of instances of each.

[
    {"office":"abc","count":1},
    {"office":"def","count":2},
    {"office":"ghi","count":1}
]

The following i have tried is

ko.utils.arrayForEach(officeLines, function (item, indx)
{
    var office = item.Office;
    if (test[office] == null)
    {
        test.push({ office: office, count: 1 });
    }
    else
    {
        test["office"][office] += 1;
    }
});

but this gives me a single item for each Office in the original array.

1
  • @FelixKling yeah it really does. i just noticed that. Its for a prototype of a page and not really thinking. Commented Jun 26, 2013 at 15:27

5 Answers 5

6

Looks like you need a dictionary or hash to create the list of unique offices, then turn that into an array for your final result.

In your code you are confusing array syntax with associative array (literal object) syntax.

Difference Example:

var array = [];
array[0] = { bar: 'foo' }; // note the numeric index accessor/mutator

var obj = {};
obj['property'] = { bar: 'foo' }; // the brackets serve as a property accessor/mutator

To fix your code:

var hash = {}; // object literal
ko.utils.arrayForEach(officeLines, function (item, indx) {
    var office = item.Office;
    if (!hash.hasOwnProperty(office)) {
        hash[office] = { office: office, count: 1 };
    }
    else {
        hash[office].count++;
    }
});

// make array
var test = [];
for(var office in hash)
    if(hash.hasOwnProperty(office))
        test.push(hash[office]);
Sign up to request clarification or add additional context in comments.

5 Comments

In your last section, since you're looping through the properties that you know are there, isn't that hasOwnProperty part unnecessary?
@Josiah Ruddell you have a little error, in line 4 should be !hash.hasOwnProperty(office) instead of !test.hasOwnProperty
@Joe still need the check because members could have been added to the Object.prototype and would tag along with any object instance.
Good call - that's actually bit me before and caused a bug. I should have remembered that.
The sad thing about hasOwnProperty is that it is so much slower: jsperf.com/hasownproperty-vs-in/2.
2

You seem to be mixing arrays and objects here.

if (test[office] == null)

will test whether the array test has a property abc, def etc. This condition will always be true because arrays don't have such properties. You usually add properties with numeric properties to an array, e.g. with .push like you do as well.

Objects on the other hand can have arbitrary properties.

Now regarding:

test["office"][office] += 1;

This accesses the property office of the array (which does not exist) and then the properties abc, def, etc (which of course don't exist either, since test.office does not exist in the first place).


You have to decide whether you want test to be an array or an object.
If you choose an object, aggregating the data will be easier since you can easily test for existence of an object property.
If you want an array, you can either convert the object to an array, or if you use one from the start, you'd have to iterate over the elements of the array and find the right one. That solution is less preferable, since the runtime will be O(n^2).

Object:

var test = {};

ko.utils.arrayForEach(officeLines, function (item, indx) {
    var office = item.Office;
    if (!test[office]) {
        hash[office] = 1
    }
    else {
        hash[office] += 1;
    }
});

test would then look like:

{
    abc: 1,
    def: 2,
    ghi: 1
}

and it would be easy to create an array from that like you want it.

Array:

var test = [];

ko.utils.arrayForEach(officeLines, function (item, indx) {
    var office = item.Office;
    for (var i = 0, l = test.length; i < l; i++) {
        if (test[i].office === office) {
            test[i].count += 1;
            return;
        }
    }
    test.push({office: office, count: 1});
});

I hope you can already see from the nested loop (for inside forEach) that this is not an optimal solution.


Further reading material:

Comments

0

What about something like this?

var test = {};
ko.utils.arrayForEach(officeLines, function (item, indx)
{
    var office = item.Office;
    if (typeof test[office] === "undefined")
    {
        test[office] = 1;
    }
    else
    {
        test[office]++;
    }
});
// back to array
var array = [];
for (var office in test) {
    array.push({ office: office, count: test[propertyName] });
}

I think you might not have to convert back to an array, because you might be able to use $index to get the office name, then $data for the count in a foreach in knockoutjs.

Comments

0
var officeLines = [{"Office":"abc", "Name":"ABC", "Total":0},
                   {"Office":"def", "Name":"DEF", "Total":11},
                   {"Office":"def", "Name":"DEF", "Total":1},
                   {"Office":"ghi", "Name":"GHI", "Total":1111}];

var test = {};

for (office_idx in officeLines) {
    var office = officeLines[office_idx];
    if (test[office['Office']]) {
        test[office['Office']]['count'] += 1;
    } else {
        test[office['Office']] = {office: office, count: 1};
    }
}
var results = []
for (office_id in test){
    results.push(test[office_id]);
}

Comments

-1

How about instead of pushing the results to an array you push them to an object like so:

var officeLines = [{"Office":"abc", "Name":"ABC", "Total":0},
{"Office":"def", "Name":"DEF", "Total":11},
{"Office":"def", "Name":"DEF", "Total":1},
{"Office":"ghi", "Name":"GHI", "Total":1111}];
var offices = {};
ko.utils.arrayForEach(officeLines, function (item, indx)
{
    var office = item.Office;
    if (!offices[office])
    {

        offices[office] = 1;
    }
    else
    {
        offices[office]++;
    }
});

document.write(JSON.stringify(offices)); // -> {"abc":1,"def":2,"ghi":1}

http://jsfiddle.net/zd5Db/

1 Comment

@FelixKling Thanks I modified and posted a jsfiddle.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.